|
|
|
@ -2,13 +2,17 @@ package reflect
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"encoding/json" |
|
|
|
|
"fmt" |
|
|
|
|
"reflect" |
|
|
|
|
"strings" |
|
|
|
|
|
|
|
|
|
cnet "github.com/xtls/xray-core/common/net" |
|
|
|
|
cserial "github.com/xtls/xray-core/common/serial" |
|
|
|
|
"github.com/xtls/xray-core/infra/conf" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func MarshalToJson(v interface{}) (string, bool) { |
|
|
|
|
if itf := marshalInterface(v, true); itf != nil { |
|
|
|
|
func MarshalToJson(v interface{}, insertTypeInfo bool) (string, bool) { |
|
|
|
|
if itf := marshalInterface(v, true, insertTypeInfo); itf != nil { |
|
|
|
|
if b, err := json.MarshalIndent(itf, "", " "); err == nil { |
|
|
|
|
return string(b[:]), true |
|
|
|
|
} |
|
|
|
@ -16,7 +20,7 @@ func MarshalToJson(v interface{}) (string, bool) {
|
|
|
|
|
return "", false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalTypedMessage(v *cserial.TypedMessage, ignoreNullValue bool) interface{} { |
|
|
|
|
func marshalTypedMessage(v *cserial.TypedMessage, ignoreNullValue bool, insertTypeInfo bool) interface{} { |
|
|
|
|
if v == nil { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
@ -24,36 +28,67 @@ func marshalTypedMessage(v *cserial.TypedMessage, ignoreNullValue bool) interfac
|
|
|
|
|
if err != nil { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
r := marshalInterface(tmsg, ignoreNullValue) |
|
|
|
|
if msg, ok := r.(map[string]interface{}); ok { |
|
|
|
|
r := marshalInterface(tmsg, ignoreNullValue, insertTypeInfo) |
|
|
|
|
if msg, ok := r.(map[string]interface{}); ok && insertTypeInfo { |
|
|
|
|
msg["_TypedMessage_"] = v.Type |
|
|
|
|
} |
|
|
|
|
return r |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalSlice(v reflect.Value, ignoreNullValue bool) interface{} { |
|
|
|
|
func marshalSlice(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) interface{} { |
|
|
|
|
r := make([]interface{}, 0) |
|
|
|
|
for i := 0; i < v.Len(); i++ { |
|
|
|
|
rv := v.Index(i) |
|
|
|
|
if rv.CanInterface() { |
|
|
|
|
value := rv.Interface() |
|
|
|
|
r = append(r, marshalInterface(value, ignoreNullValue)) |
|
|
|
|
r = append(r, marshalInterface(value, ignoreNullValue, insertTypeInfo)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return r |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalStruct(v reflect.Value, ignoreNullValue bool) interface{} { |
|
|
|
|
func isNullValue(f reflect.StructField, rv reflect.Value) bool { |
|
|
|
|
if rv.Kind() == reflect.String && rv.Len() == 0 { |
|
|
|
|
return true |
|
|
|
|
} else if !isValueKind(rv.Kind()) && rv.IsNil() { |
|
|
|
|
return true |
|
|
|
|
} else if tag := f.Tag.Get("json"); strings.Contains(tag, "omitempty") { |
|
|
|
|
if !rv.IsValid() || rv.IsZero() { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func toJsonName(f reflect.StructField) string { |
|
|
|
|
if tags := f.Tag.Get("protobuf"); len(tags) > 0 { |
|
|
|
|
for _, tag := range strings.Split(tags, ",") { |
|
|
|
|
if before, after, ok := strings.Cut(tag, "="); ok && before == "json" { |
|
|
|
|
return after |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if tag := f.Tag.Get("json"); len(tag) > 0 { |
|
|
|
|
if before, _, ok := strings.Cut(tag, ","); ok { |
|
|
|
|
return before |
|
|
|
|
} else { |
|
|
|
|
return tag |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return f.Name |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalStruct(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) interface{} { |
|
|
|
|
r := make(map[string]interface{}) |
|
|
|
|
t := v.Type() |
|
|
|
|
for i := 0; i < v.NumField(); i++ { |
|
|
|
|
rv := v.Field(i) |
|
|
|
|
if rv.CanInterface() { |
|
|
|
|
ft := t.Field(i) |
|
|
|
|
name := ft.Name |
|
|
|
|
value := rv.Interface() |
|
|
|
|
tv := marshalInterface(value, ignoreNullValue) |
|
|
|
|
if tv != nil || !ignoreNullValue { |
|
|
|
|
if !ignoreNullValue || !isNullValue(ft, rv) { |
|
|
|
|
name := toJsonName(ft) |
|
|
|
|
value := rv.Interface() |
|
|
|
|
tv := marshalInterface(value, ignoreNullValue, insertTypeInfo) |
|
|
|
|
r[name] = tv |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -61,7 +96,7 @@ func marshalStruct(v reflect.Value, ignoreNullValue bool) interface{} {
|
|
|
|
|
return r |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalMap(v reflect.Value, ignoreNullValue bool) interface{} { |
|
|
|
|
func marshalMap(v reflect.Value, ignoreNullValue bool, insertTypeInfo bool) interface{} { |
|
|
|
|
// policy.level is map[uint32] *struct
|
|
|
|
|
kt := v.Type().Key() |
|
|
|
|
vt := reflect.TypeOf((*interface{})(nil)) |
|
|
|
@ -71,7 +106,7 @@ func marshalMap(v reflect.Value, ignoreNullValue bool) interface{} {
|
|
|
|
|
rv := v.MapIndex(key) |
|
|
|
|
if rv.CanInterface() { |
|
|
|
|
iv := rv.Interface() |
|
|
|
|
tv := marshalInterface(iv, ignoreNullValue) |
|
|
|
|
tv := marshalInterface(iv, ignoreNullValue, insertTypeInfo) |
|
|
|
|
if tv != nil || !ignoreNullValue { |
|
|
|
|
r.SetMapIndex(key, reflect.ValueOf(&tv)) |
|
|
|
|
} |
|
|
|
@ -87,27 +122,63 @@ func marshalIString(v interface{}) (r string, ok bool) {
|
|
|
|
|
ok = false |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
if iStringFn, ok := v.(interface{ String() string }); ok { |
|
|
|
|
return iStringFn.String(), true |
|
|
|
|
} |
|
|
|
|
return "", false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalKnownType(v interface{}, ignoreNullValue bool) (interface{}, bool) { |
|
|
|
|
func serializePortList(portList *cnet.PortList) (interface{}, bool) { |
|
|
|
|
if portList == nil { |
|
|
|
|
return nil, false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
n := len(portList.Range) |
|
|
|
|
if n == 1 { |
|
|
|
|
if first := portList.Range[0]; first.From == first.To { |
|
|
|
|
return first.From, true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
r := make([]string, 0, n) |
|
|
|
|
for _, pr := range portList.Range { |
|
|
|
|
if pr.From == pr.To { |
|
|
|
|
r = append(r, pr.FromPort().String()) |
|
|
|
|
} else { |
|
|
|
|
r = append(r, fmt.Sprintf("%d-%d", pr.From, pr.To)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return strings.Join(r, ","), true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalKnownType(v interface{}, ignoreNullValue bool, insertTypeInfo bool) (interface{}, bool) { |
|
|
|
|
switch ty := v.(type) { |
|
|
|
|
case cserial.TypedMessage: |
|
|
|
|
return marshalTypedMessage(&ty, ignoreNullValue), true |
|
|
|
|
return marshalTypedMessage(&ty, ignoreNullValue, insertTypeInfo), true |
|
|
|
|
case *cserial.TypedMessage: |
|
|
|
|
return marshalTypedMessage(ty, ignoreNullValue), true |
|
|
|
|
return marshalTypedMessage(ty, ignoreNullValue, insertTypeInfo), true |
|
|
|
|
case map[string]json.RawMessage: |
|
|
|
|
return ty, true |
|
|
|
|
case []json.RawMessage: |
|
|
|
|
return ty, true |
|
|
|
|
case *json.RawMessage: |
|
|
|
|
return ty, true |
|
|
|
|
case json.RawMessage: |
|
|
|
|
case *json.RawMessage, json.RawMessage: |
|
|
|
|
return ty, true |
|
|
|
|
case *cnet.IPOrDomain: |
|
|
|
|
if domain := v.(*cnet.IPOrDomain); domain != nil { |
|
|
|
|
return domain.AsAddress().String(), true |
|
|
|
|
} |
|
|
|
|
return nil, false |
|
|
|
|
case *cnet.PortList: |
|
|
|
|
npl := v.(*cnet.PortList) |
|
|
|
|
return serializePortList(npl) |
|
|
|
|
case *conf.PortList: |
|
|
|
|
cpl := v.(*conf.PortList) |
|
|
|
|
return serializePortList(cpl.Build()) |
|
|
|
|
case cnet.Address: |
|
|
|
|
if addr := v.(cnet.Address); addr != nil { |
|
|
|
|
return addr.String(), true |
|
|
|
|
} |
|
|
|
|
return nil, false |
|
|
|
|
default: |
|
|
|
|
return nil, false |
|
|
|
|
} |
|
|
|
@ -138,9 +209,9 @@ func isValueKind(kind reflect.Kind) bool {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func marshalInterface(v interface{}, ignoreNullValue bool) interface{} { |
|
|
|
|
func marshalInterface(v interface{}, ignoreNullValue bool, insertTypeInfo bool) interface{} { |
|
|
|
|
|
|
|
|
|
if r, ok := marshalKnownType(v, ignoreNullValue); ok { |
|
|
|
|
if r, ok := marshalKnownType(v, ignoreNullValue, insertTypeInfo); ok { |
|
|
|
|
return r |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -152,19 +223,27 @@ func marshalInterface(v interface{}, ignoreNullValue bool) interface{} {
|
|
|
|
|
if k == reflect.Invalid { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
if isValueKind(k) { |
|
|
|
|
|
|
|
|
|
if ty := rv.Type().Name(); isValueKind(k) { |
|
|
|
|
if k.String() != ty { |
|
|
|
|
if s, ok := marshalIString(v); ok { |
|
|
|
|
return s |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return v |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// fmt.Println("kind:", k, "type:", rv.Type().Name())
|
|
|
|
|
|
|
|
|
|
switch k { |
|
|
|
|
case reflect.Struct: |
|
|
|
|
return marshalStruct(rv, ignoreNullValue) |
|
|
|
|
return marshalStruct(rv, ignoreNullValue, insertTypeInfo) |
|
|
|
|
case reflect.Slice: |
|
|
|
|
return marshalSlice(rv, ignoreNullValue) |
|
|
|
|
return marshalSlice(rv, ignoreNullValue, insertTypeInfo) |
|
|
|
|
case reflect.Array: |
|
|
|
|
return marshalSlice(rv, ignoreNullValue) |
|
|
|
|
return marshalSlice(rv, ignoreNullValue, insertTypeInfo) |
|
|
|
|
case reflect.Map: |
|
|
|
|
return marshalMap(rv, ignoreNullValue) |
|
|
|
|
return marshalMap(rv, ignoreNullValue, insertTypeInfo) |
|
|
|
|
default: |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|