使用panic/recover作为测试成功类型断言的手段是否可以接受?

huangapple go评论91阅读模式
英文:

Is it ok to use panic/recover as a means for testing successful type assertion?

问题

我一直在努力寻找一种方法来解析嵌套的JSON响应,而不需要将信息映射到预定义的结构中。

使用空接口,返回的结果如下:

<pre>
map[name:My Folder parentId:1 created:2014-10-09T16:32:07+0000 deleted:false description:Sync Dir id:3 links:[map[rel:self entity:folder href:https://web.domain.org/rest/folders/3 id:3] map[href:https://web.domain.org/rest/folders/1 id:1 rel:parent entity:folder] map[entity:user href:https://web.domain.org/rest/users/1 id:1 rel:creator]] modified:2014-12-18T18:07:01+0000 permalink:https://web.domain.org/w/SpJYGQkv syncable:true type:d userId:1]
</pre>

所以我使用以下方法来导航这些信息:

func NFind(input interface{}, refs...interface{}) (output interface{}) {
	defer func() {if r := recover(); r != nil { output = nil }}()
	
	for _, ref := range refs {
		switch cur := ref.(type) {
			case string:
				output = input.(map[string]interface{})[cur]
			case int:
				output = input.([]interface{})[cur]
		} 
	}
	return output
}

func NMap(input interface{}) (output map[string]interface{}) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return nil }
	return input.(map[string]interface{})
}

func NArray(input interface{}) (output []interface{}) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return nil }
	return input.([]interface{})
}

func NString(input interface{}) (output string) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return &quot;&quot; }
	return input.(string)
}

func NFloat64(input interface{}) (output float64) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return 0 }
	return input.(float64)
} 

这种方法是否可以接受用于解析JSON字符串中的信息,还是有更好的方法?

以下是我目前使用上述方法解析正确信息的示例:

func mapCache(input map[string]interface{}, valType string) {
	fmt.Println(input)
	var (
		name string
		href string
		rel string
		links []interface{}
		myMap map[string]interface{}
	)
	
	if name = NString(NFind(input, &quot;name&quot;)); name == &quot;&quot; { return }
	if links = NArray(NFind(input, &quot;links&quot;)); links == nil { return }
	
	for i := 0; i &lt; len(links); i++ {
		if myMap = NMap(links[i]); myMap == nil { return }
		if rel = NString(myMap[&quot;rel&quot;]); rel == &quot;&quot; { return }
		if rel == &quot;self&quot; {
			if href = NString(myMap[&quot;href&quot;]); href == &quot;&quot; { return }
		}
	}
	CacheDB.Set(valType, name, href, false)
}

希望能对你有所帮助!谢谢!

英文:

I've been working on a way of trying to parse through nested JSON responses without mapping the information to a predefined struct.

With a blank interface it comes back as:

<pre>
map[name:My Folder parentId:1 created:2014-10-09T16:32:07+0000 deleted:false description:Sync Dir id:3 links:[map[rel:self entity:folder href:https://web.domain.org/rest/folders/3 id:3] map[href:https://web.domain.org/rest/folders/1 id:1 rel:parent entity:folder] map[entity:user href:https://web.domain.org/rest/users/1 id:1 rel:creator]] modified:2014-12-18T18:07:01+0000 permalink:https://web.domain.org/w/SpJYGQkv syncable:true type:d userId:1]
</pre>

So I'm using the following to navigate this information:

func NFind(input interface{}, refs...interface{}) (output interface{}) {
	defer func() {if r := recover(); r != nil { output = nil }}()
	
	for _, ref := range refs {
		switch cur := ref.(type) {
			case string:
				output = input.(map[string]interface{})[cur]
			case int:
				output = input.([]interface{})[cur]
		} 
	}
	return output
}

func NMap(input interface{}) (output map[string]interface{}) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return nil }
	return input.(map[string]interface{})
}

func NArray(input interface{}) (output []interface{}) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return nil }
	return input.([]interface{})
}

func NString(input interface{}) (output string) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return &quot;&quot; }
	return input.(string)
}

func NFloat64(input interface{}) (output float64) {
	defer func() {if r := recover(); r != nil {}}()
	if input == nil { return 0 }
	return input.(float64)
} 

Is this an acceptable way of parsing information from JSON strings, or is there a more preferable method?

Here is the example of using the above to parse out the correct information I'm currently using:

func mapCache(input map[string]interface{}, valType string) {
	fmt.Println(input)
	var (
		name string
		href string
		rel string
		links []interface{}
		myMap map[string]interface{}
	)
	
	if name = NString(NFind(input, &quot;name&quot;)); name == &quot;&quot; { return }
	if links = NArray(NFind(input, &quot;links&quot;)); links == nil { return }
	
	for i := 0; i &lt; len(links); i++ {
		if myMap = NMap(links[i]); myMap == nil { return }
		if rel = NString(myMap[&quot;rel&quot;]); rel == &quot;&quot; { return }
		if rel == &quot;self&quot; {
			if href = NString(myMap[&quot;href&quot;]); href == &quot;&quot; { return }
		}
	}
	CacheDB.Set(valType, name, href, false)
}

Any insight would be appreciated! Thanks!

答案1

得分: 7

请参考类型断言的规范说明

在赋值或初始化的特殊形式中使用类型断言,会产生一个额外的无类型布尔值。如果断言成立,ok 的值为 true。否则,ok 的值为 false,v 的值为类型 T 的零值。在这种情况下不会发生运行时恐慌。

与使用错误处理来检查类型相比,这种方法更快速、更简洁。

因此,你可以将以下代码重写为:

func NMap(input interface{}) map[string]interface{} {
    if m, ok := input.(map[string]interface{}); ok {
        return m
    }
    return nil
}

你还可以考虑使用类似 github.com/zazab/zhash 的库,以便更轻松地处理 map[string]interface{} 类型。当然,你也可以尝试使用 encoding/json 中的现有模式来实现。

英文:

Check the specification for type assertions:

>A type assertion used in an assignment or initialization of the special form
>
v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)

> yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.

This is faster and less hacky than using error handling to check the type.

So you could rewrite

func NMap(input interface{}) map[string]interface{} {
    defer func() {if r := recover(); r != nil {}}()
    if input == nil { return nil }
    return input.(map[string]interface{})
}

as

func NMap(input interface{}) map[string]interface{} {
    if m, ok := input.(map[string]interface{}); ok {
        return m
    }
    return nil
}

You might also consider a library like github.com/zazab/zhash for making map[string]interface{} easier to work with. Or, of course, try to work out how one of encoding/json's existing modes can do it.

huangapple
  • 本文由 发表于 2014年12月20日 14:17:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/27577610.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定