2017-09-25 2 views
0

Ich muss JServerArray zu einem booleschen Wert deserialisieren. Wenn ein Array existiert und nicht leer ist, sollte der Wert auf "true" gesetzt werden. Das Problem ist, meine benutzerdefinierte Deserializer, während funktional, Deserialisierung der Rest der Felder bricht - sie werden auf Null gesetzt.Jackson benutzerdefinierten Deserializer bricht Deserialisierung anderer Felder

Objekt:

private static class TestObject { 
    private String name; 

    @JsonProperty("arr") 
    @JsonDeserialize(using = Deserializer.class) 
    private Boolean exists = null; 

    private Integer logins; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public Boolean getExists() { 
     return exists; 
    } 

    public void setExists(boolean exists) { 
     this.exists = exists; 
    } 

    public Integer getLogins() { 
     return logins; 
    } 

    public void setLogins(Integer logins) { 
     this.logins = logins; 
    } 

    @Override 
    public String toString() { 
     return "TestObject{" + 
       "name='" + name + '\'' + 
       ", exists=" + exists + 
       ", logins=" + logins + 
       '}'; 
    } 
} 

Deserializer:

public class Deserializer extends JsonDeserializer<Boolean> { 
    @Override 
    public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) 
     throws IOException { 
     if (jp.getCurrentToken() == JsonToken.START_ARRAY) { 
      return true; 
     } 
     return false; 
    } 
} 

-Test

@Test 
public void test() throws JsonParseException, IOException { 
    Boolean result = deserialize(); 
} 

private Boolean deserialize() throws IOException, JsonParseException, 
     JsonProcessingException { 
    TestObject testObject = mapper.readValue("{\n" + 
        " \"arr\": [\n" + 
        "  {\"value\": \"New\"}\n" + 
        " ],\n" + 
        " \"name\": \"name\",\n" + 
        " \"logins\": 36" + 
        "}", 
        TestObject.class); 

    System.out.println(testObject.toString()); 

    return testObject.getExists(); 
} 

Wenn ich die "arr" Array entfernen oder auf dem Boden des Json, alles ist in Ordnung bewegen . Wenn ich es oben lasse - TestObject{name='null', exists=true, logins=null}.

Es gab eine ähnliche Frage (Jackson Custom Deserializer breaks default ones), aber leider hat es keine Antworten. Da der Code ordnungsgemäß funktioniert, wenn ich Json neu anordne, sieht es nicht so aus, als würde Custom Deserializer für alle Felder verwendet, sondern Jackson stoppt die Deserialisierung, wenn es den benutzerdefinierten Deserializer ausführt.

+1

Mein Vorschlag ist es, ein Array nicht zu einem booleschen zu deserialisieren. Deserialisieren Sie das Array zu einem Array/list/set und lassen Sie 'getExists' return 'arr! = Null' oder ähnliches. – Paul

+0

@Paul Das funktioniert tatsächlich, danke für die Idee, irgendwie habe ich nicht daran gedacht. Das Problem ist, dass es ein bisschen hässlich und undurchsichtig aussieht, aber wenn es keinen anderen Weg gibt, werde ich es so machen. –

+0

Manchmal muss man das hübschere von zwei hässlichen Lösungen wählen :) Ich bin froh, dass ich helfen konnte ... Ich werde meinen Kommentar eine Antwort geben. – Paul

Antwort

1

Ihre Deserializer nicht in den Inhalt des Arrays interessiert sein aber muss immer noch die Lesemarkierung auf dem Parser vorrücken.

Sie könnten den Wert von arr auf einmal lesen und entscheiden, je nach der Größe der Sammlung:

@Override 
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) 
     throws IOException { 
    JsonNode node = jp.getCodec().readTree(jp); 
    return node.size() != 0; 
} 

Der Entscheidung über die Größe der Sammlung und nicht auf die Existenz eine Sammlung an Alles ist notwendig, weil entweder Ihr stringifiziertes Objekt einen arr enthält oder Deserializer.deserialize() niemals ausgeführt wird. Die Eigenschaft exist wird in diesem Fall null sein. Die einzige mögliche Semantik zum Ausdrücken existiert nicht ist eine leere Sammlung.

Nur für die Neugier habe ich einen zweiten expliziten Weg versucht, den Parser auf Kurs zu halten. Für den realen Gebrauch ist die obige Version definitiv vorzuziehen.

@Override 
public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) 
     throws IOException { 
    if (jp.currentToken() == JsonToken.START_ARRAY) { 
     jp.nextToken(); 
     int recursionLevel = 1; 
     while(recursionLevel > 0) { 
      switch (jp.currentToken()) { 
       case START_ARRAY: 
        // just in case of nested arrays 
        recursionLevel++; 
        break; 
       case END_ARRAY: 
        recursionLevel--; 
        break; 
      } 
      jp.nextToken(); 
     } 
     return true; 
    } 
    return false; 
} 
+0

Dies ist die richtige Lösung. 'JsonDeserializer' javadoc erwähnt, dass Sie die Lesemarke auf das nächste Element verschieben müssen, bevor Sie vom Deserializer zurückkehren, damit Jackson mit dem nächsten Element arbeiten kann. In meinem Fall ist es ein Array, also war es nicht genug, es einmal zu bewegen, wie ich es versuchte, du musst dich so oft bewegen, bis du 'END_ARRAY' erreichst. 'readTree' liest, soweit ich von javadoc unterscheiden kann, das gesamte Array, erstellt einen Baum und bringt den Lesemarker in einem Paket voran. –

1

Mein Vorschlag ist es, ein Array nicht zu einem booleschen zu deserialisieren. Deserialize das Array zu einem Array/Liste/Set und getExists zurückgeben arr != null oder einige solcher.

Zum Beispiel, wenn die JSON sind Sie sieht wie folgt aus Deserialisieren:

{ 
    "arr": [{"value": "New"}], 
    "name": "name", 
    "logins": 36 
} 

Schreiben Sie Entität wie folgt aus:

private static class TestObject 
{ 
    private List<Map<String, String>> arr; 

    // other getters/setters removed for brevity 

    public boolean getExists() 
    { 
    return arr !=null && !arr.isEmpty(); 
    } 
} 
0

ist es tatsächlich ein dritte Weg, es zu tun, das paßt meine Fall - JsonArray wird immer mindestens ein Element enthalten, wenn es nicht fehlen, so alles, was ich brauche, ist die Lese Marker richtig voraus und dann wieder true:

if (jp.getCurrentToken().equals(JsonToken.START_ARRAY)) { 
    while (!jp.getCurrentToken().equals(JsonToken.END_ARRAY)){ 
     jp.nextToken(); 
    } 
} 
return true; 

Sofern Sie den Inhalt des Arrays für einige zusätzliche Arbeit nicht brauchen, nur die Markierung bewegen, ist ein paar Millisekunden schneller als readTree.

Verwandte Themen