是否可以在未知的json结构中重写字符串?

时间:2017-12-05 06:16:46

标签: json go

我需要解析任意json,编码/重写任何字符串键或超过85个字符的值(想想一种简短的URL服务)并将其编组回原始json。  例如像{"Name": "Ed", "Text": "long characters....85 chars"}这样的文档应该像这样{"Name": "Ed", "Text": "short://ID=3403"}。这个例子很简单,但我必须处理具有嵌套对象和数组以及基本未知结构的复杂结构。

我的问题是:是否可以使用已知库或甚至标准编码/ json包来执行此操作?  我实际需要的是相当于type string package com.ama.ist.controller; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.ama.ist.model.CustomErrorType; import com.ama.ist.model.Patch; import com.ama.ist.service.PatchService; @RestController public class PatchController { @Autowired private PatchService patchService; @CrossOrigin(origins = "http://ipnumber:portnumber") @RequestMapping(value = "/mk", method = RequestMethod.POST) public ResponseEntity<?> createFolder(@RequestBody Patch patch) { System.out.println("patch ddest: => " + patch.getDestination()); String iscreatedstatus = patchService.create(patch); System.out.println("iscreatedstatus" + iscreatedstatus); if (!(iscreatedstatus.equals("Success"))) { System.out.println("if success" ); return new ResponseEntity<Object>(new CustomErrorType("ER",iscreatedstatus), HttpStatus.NOT_FOUND); } System.out.println("if disinda success" ); return new ResponseEntity<Object>(new CustomErrorType("OK",iscreatedstatus), HttpStatus.CREATED); } // @RequestMapping("/resource") public Map<String,Object> home() { Map<String,Object> model = new HashMap<String,Object>(); model.put("id", UUID.randomUUID().toString()); model.put("content", "Hello World"); return model; } } 接口的实现,但我们知道这是不可能的,所以我想知道我还有其他选择(除了分支标准之外) encoding / json包)

3 个答案:

答案 0 :(得分:2)

是的,可以使用标准库和一些代码。

解组接口{}

var v interface{}
if err := json.Unmarshal(data, &v); err != nil {
  // handle error
}

遍历解组的值,缩短字符串并重写:

func shorten(v interface{}) interface{} {
    switch v := v.(type) {
    case []interface{}:
        for i, e := range v {
            v[i] = shorten(e)
        }
        return v
    case map[string]interface{}:
        m := make(map[string]interface{})
        for k, e := range v {
            m[shortenString(k)] = shorten(e)
        }
        return m
    case string:
        return shortenString(v)
    default:
        return v
    }
}

函数shorten调用shortenString(s string) string将长字符串转换为short://ID=xxx

将值重新编号为JSON:

p, err := json.Marshal(shorten(v))
if err != nil {
    // handle error
}

// p is a []byte
fmt.Printf("%s\n", p)

playground example

答案 1 :(得分:1)

如果要求使用JSON中的新缩短版本替换所有超过85个字符的字符串,并且您可以假设JSON是有效的JSON(即,如果您不需要提供诊断)无效输入)然后您可以使用单个正则表达式替换。

有效JSON中的带引号的字符串可以与

匹配
/"([^\\"]|\\.)*"/

因此,您可以使用可能缩短的版本替换此模式的所有实例。

答案 2 :(得分:0)

这是一种使用json.Decoder Token()方法的方法(已测试),并避免将整个数据结构反序列化为内存。它主要是为了娱乐和为了说明如何使用该方法而完成的,但如果您的JSON文档非常大并且您希望避免内存开销,它可能会有所帮助。

// For example
r, w := os.Stdin, os.Stdout

dec := json.NewDecoder(r)
// Avoids any round-trip loss by leaving numbers un-parsed
dec.UseNumber()

// Current state (in an object or in an array; following the open
// delim, or a key, or a value). The decoder has its own stack pretty
// like this one, but it's private, so keep our own.
const (
    jsonArrayStart = iota
    jsonArrayVal
    jsonObjectStart
    jsonObjectKey
    jsonObjectVal
)
stack := []byte{}

for {
    t, err := dec.Token()
    if err == io.EOF {
        break
    } else if err != nil {
        log.Fatal(err)
    }

    switch val := t.(type) {
    case json.Delim:
        // Copy delimiters out, and push/pop the state stack as appropriate
        w.WriteString(string([]rune{rune(val)}))
        switch val {
        case '[':
            stack = append(stack, jsonArrayStart)
        case '{':
            stack = append(stack, jsonObjectStart)
        case ']', '}':
            stack = stack[:len(stack)-1]
        }
        // The rest of the cases just copy values out
    case nil:
        w.WriteString("null")
    case json.Number:
        w.WriteString(string(val))
    case bool:
        if val {
            w.WriteString("true")
        } else {
            w.WriteString("false")
        }
    case string:
        // Modify strings if called for (shortenString needs to be provided)
        if len(val) >= 85 {
            val = shortenString(val)
        }
        encoded, err := json.Marshal(val)
        if err != nil {
            log.Fatal(err)
        }
        w.Write(encoded)
    }

    if dec.More() {
        // If there's more in the current array/object, write a colon or comma
        // (if we just wrote a key/value), and set the next state.
        // Arrays start with a value, and follow with more values.
        // Objects start with a key and alternate between key and value.
        switch stack[len(stack)-1] {
        case jsonArrayStart:
            stack[len(stack)-1] = jsonArrayVal
        case jsonArrayVal:
            w.WriteString(",")
            // State remains jsonArrayVal
        case jsonObjectStart:
            stack[len(stack)-1] = jsonObjectKey
        case jsonObjectKey:
            w.WriteString(":")
            stack[len(stack)-1] = jsonObjectVal
        case jsonObjectVal:
            w.WriteString(",")
            stack[len(stack)-1] = jsonObjectKey
        }
    } else {
        if len(stack) == 0 {
            // End after the first complete value (array/object) in the stream
            break
        }
        if stack[len(stack)-1] == jsonObjectKey {
            // Should never happen
            log.Fatal("Object key without a value?")
        }
    }
}