如何从杰克逊的JSON中提取部分*原始*文本?

时间:2016-08-10 04:09:53

标签: java json jackson

请不要为我编辑问题。我的问题是关于字符串操作,改变文本流很可能会改变问题的含义并导致混淆。

可以将问题视为字符串操作问题。但我希望Jackson能够解决我的问题。

假设我收到了一个字符串{"payload":{"foo":"bar","ipsum":["lorem","lorem"]},"signature":"somehmacsign"}。当它显示时,它就像:

{
    "payload": {
        "foo": "bar",
        "ipsum": [
            "lorem",
            "lorem"
        ]
    },
    "signature": "somehmacsign"
}

如何从第11个字符{中提取其子字符串,直到}之前的,"signature"。即,{"foo":"bar","ipsum":["lorem","lorem"]}。从语义上讲,我想提取payload字段的原始字符串表示。我想它不应该涉及将JSON字符串解析为Java对象并返回String。我不想冒失去字段,间距或任何小功能的顺序,因为它意味着HMAC签名。

编辑1:重新说明此问题与Java String文字无关。

编辑2:虽然说起来可能有点早,但我的问题一般没有明显的现成解决方案(如何部分提取/解析JSON字符串)。事实上,我自己发现我的懒惰/部分解析理论有点冗长。它需要太多的传递来定位深度嵌套的节点。

特别是对于我的情况,我发现在请求正文中附加签名是一个坏主意,因为它给接收方带来了困难。我正在考虑将签名移到HTTP标头,也许是X-Auth-Token

编辑3:事实证明,我的结论太早了。 Cassio的答案通过使用自定义反序列化器和魔法skipChildren完美地解决了这个问题!

4 个答案:

答案 0 :(得分:8)

您可以将签名移动到HTTP标头以使事情更简单。

但是,如果要将签名保留在请求有效内容中,请执行以下步骤。

创建自定义反序列化程序

创建一个可以获取原始JSON字符串值的自定义反序列化器:

public class RawJsonDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
           throws IOException, JsonProcessingException {

        long begin = jp.getCurrentLocation().getCharOffset();
        jp.skipChildren();
        long end = jp.getCurrentLocation().getCharOffset();

        String json = jp.getCurrentLocation().getSourceRef().toString();
        return json.substring((int) begin - 1, (int) end);
    }
}

创建一个Java类来保存值

创建一个Java类来保存值(注意@JsonDeserialize注释):

public class Request {

    @JsonDeserialize(using = RawJsonDeserializer.class)
    private String payload;

    private String signature;

    // Getters and setters ommitted
}

解析JSON

使用ObjectMapper解析JSON,Jackson将使用您在上面定义的解串器:

String json = "{\"payload\":{\"foo\":\"bar\",\"ipsum\":[\"lorem\"," +
              "\"lorem\"]},\"signature\":\"somehmacsign\"}";

ObjectMapper mapper = new ObjectMapper();
Request request = mapper.readValue(json, Request.class);

如果payload是无效的JSON,杰克逊将抛出异常。

答案 1 :(得分:1)

这是一个减去你的字符串的理论解决方案。

您一次只读取一个JSON字符并计算每个{找到的}和每个{,一旦两者具有相同的数量,就会得到块结尾的索引,因为你有起始索引(你找到了第一个开始括号),你可以减去字符串。从}public static String extractFirstBlock(String s, String key){ int length = s.length() - 1; int start = -1; int i = s.indexOf(key); //This should be done by reading characters one by one to check if this is a key or a value. if(i == -1) return null; i += key.length(); int cntOpen = 0; while(i < length){ char c = s.charAt(i); if(c == '{'){ //Need to check for character into String value too if(cntOpen++ == 0) start = i; } else if(c == '}'){ if(--cntOpen == 0){ return s.substring(start, i + 1); } } ++i; } return null; }

我将处理基本代码。

编辑:

这是一个简单的代码,不是为了性能而创建的,也不是像这样保存的。只是为了证明这很简单。

public static void main(String[] args){
    System.out.println(extractFirstBlock("{\"payday\":{\"fae\":\"boo\",\"ipsum\":[\"lom\",\"lorem\"]},\"signature\":\"somehmacsign\"},{\"payload\":{\"foo\":\"bar\",\"ipsum\":[\"lorem\",\"lorem\"]},\"signature\":\"somehmacsign\"}", "payload"));
    }

首先会搜索您要求的密钥。然后,它将读取以下字符串以查找块并将其返回。如果找不到任何内容,则返回null。

{"foo":"bar","ipsum":["lorem","lorem"]}

结果:

test = function()
{
    //Your code
    setTimeout(test, 1000);
}

改善:

  • 跟踪光标的位置,如果是字符串
  • 使用相同的逻辑来查找密钥。
  • 检查转义字符

答案 2 :(得分:0)

您可以使用simple-json.jarJSONObject

中提取该对象

可下载的Jar Link - simple-json.jar Download Link

Maven Jar导入Maven Repository pom syntax

你的实际对象是

{
    "payload": {
        "foo": "bar",
        "ipsum": [
            "lorem",
            "lorem"
        ]
    },
    "signature": "somehmacsign"
} // hold this complete object in any string reference Variable.
  

这里我假设String jsonString保存完整的json对象,如上所述。

import org.json.simple.parser.JSONParser; 
import org.json.simple.parser.JSONObject;

// implemetation logic for extracting jsonObject.

private JSONObject extractFirstBlock(String jsonString) throws ......{

   JSONObject jsonObj, desiredObject;
   JSONParser parser=new JSONParser();  // parser to parse string to JSONObject
   jsonObj = (JSONObject) parser.parse(jsonString); // parse the Object using parse Method.
   desiredObject = (JSONObject) jsonObj.get("payload"); // this Object contains your desired output which you wish to receive.

}

desiredObject中,您将获得期望值为JSONObject。

答案 3 :(得分:0)

在杰克逊,你将不得不做类似下面的事情。它会将json读作令牌,然后您需要条件来决定如何处理令牌。所以在这里,我可以进入JSON提取字段“有效负载”并继续追加,直到我遇到“签名”。此外,令牌可以提供采取行动(开始,场,价值等)的“标签”的类型(它是ENUM)。

public static void main(String[] args) {
        StringBuffer writer = new StringBuffer();
        try (FileInputStream fis = new FileInputStream("C:\\DevelopmentTools\\3.Code\\sample.json")) {
            boolean payloadFound = false;
            boolean signatureFound = false;

            JsonFactory jf = new JsonFactory();
            JsonParser jp = jf.createParser(fis);
            jp.nextToken();
            while (jp.hasCurrentToken()) {
                String text = jp.getText();
                if ("payload".equals(text)) {
                    System.out.println("payload found");
                    payloadFound = true;
                    jp.nextToken();
                }
                if ("signature".equals(text)) {
                    System.out.println("signature found");
                    signatureFound = true;
                }


                if (payloadFound && !signatureFound) {

                }
                jp.nextToken();
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }