1

Say that I have following objects:

public class ComplexJacksonObject extends BaseJsonObject {
    public int Start;
    public int Count;
    public Person MyPerson;

    public class Person extends BaseJsonObject {
        public String Firstname;
        public String Lastname;
        public Address Where;
    }

    public class Address extends BaseJsonObject {
        public String Street;
        public int Number;
    }
}

Obviously when I request JSON of this with Jackson I get something like:

public String toJson(ComplexJacksonObject obj) {
    try {
        return generateMapper().writeValueAsString(obj);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
        return null;
    }
}

// returned: {"MyPerson":{"Firstname":"First","Lastname":"Last","Where":{"Street":"Street","Number":15}},"Count":1,"Start":2}

However what I need for QueryString is that top property pairs are converted to Key=Value& format, so something like:

MyPerson={"Firstname":"First","Lastname":"Last","Where":{"Street":"Street","Number":15}}&Count=1&Start=2

Plus of course MyPerson=[This_Part_Needs_To_Be_Url_Encoded].

Is there any generic method in Jackson that would do this for me automatically? Or will I be forced to come up with something my own? Some String replacement regex? Any ideas?

nikib3ro
  • 20,366
  • 24
  • 120
  • 181
  • 1
    Jackson produces JSON. If you only want `Person` to be JSON, pass that to the `ObjectMapper` and build the rest yourself. – Sotirios Delimanolis Feb 01 '14 at 00:46
  • 1
    You might find this discussion useful. http://stackoverflow.com/questions/739689/how-to-convert-a-java-object-bean-to-key-value-pairs-and-vice-versa – Nick Humrich Feb 01 '14 at 00:50
  • @SotiriosDelimanolis Yeah, I could build request pretty easy manually, but I'm looking for solution that is generic. I'm thinking that maybe I should detect if object is certain type and if it is I parse it into JSON, otherwise I just spit it out as key=value pair. Any idea how I could do this (reflection)? EDIT: Looking into this: http://stackoverflow.com/questions/2989560/how-to-get-the-fields-in-an-object-via-reflection – nikib3ro Feb 01 '14 at 00:53
  • With reflection, simply get all the declared fields. Depending on your serialization rules, if it's a complex type, serialize it with JSON and put it as `fieldName={json here}`. If it's a simple type like a primitive or a `String`, put it directly. Concatenate all the fields with `&`. – Sotirios Delimanolis Feb 01 '14 at 00:55
  • Yeah, seems that's what I'll do since Regex guys are cranky as usual... will post code for others to benefit as soon as I do it. – nikib3ro Feb 01 '14 at 00:56

3 Answers3

1

[Edit] NOTE: I misunderstood the question. My answer below answers how to parse the JSON and get a Java object. You wanted to get the Key value pairs where JSON is the value for the object. The below answer will not answer that question. Sorry for the confusion.

You can fix this issue by using Jackson annotations to the java model and adding a "type" to the JSON object. You might want to research it for your purposes but here is an example from some code I have done in the past.

public class Requirement {
  private String title;
  private String reqId;

  @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
  @JsonSubTypes({
      @JsonSubTypes.Type(value=CountRequirementList.class, name="COUNT"),
      @JsonSubTypes.Type(value=AndRequirementList.class, name="AND"),
      @JsonSubTypes.Type(value=OrRequirementList.class, name="OR")
  })
  private List<RequirementList>  fulfillments;

where the baseObject is the RequirementList and the class names are types of the requirements list. To make things easier going back and forth from JSON, it is sometimes convenient to just add a type to the object. I have included more code below in case it helps. (note: I did not include all the getters and setters that are needed for Jackson)

public abstract class RequirementList {
  private LogicType type;
  private String id;
  private String title;
  private String description;
  protected float requiredCount; //For count type subclass. Designed to be count of credits
  private List<Object> fulfillments;
}



public class OrRequirementList extends RequirementList {

  public OrRequirementList() {
    super();
    super.setType(LogicType.OR);
  }
}
Nick Humrich
  • 14,905
  • 8
  • 62
  • 85
1

See my answer for this question How to serialize ANY Object into a URI?. You have to add only URL encoding to my solution. For example you can use UrlEncoder#encode(String s, String enc) method.

Community
  • 1
  • 1
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
0

OK,

Here is the holder object:

public class ComplexJacksonObject extends BaseJsonObject {
    public int Start;
    public int Count;
    public Person MyPerson;
    public List<String> Strings;

    public class Person extends BaseJsonObject {
        public String Firstname;
        public String Lastname;
        public Address Where;
    }

    public class Address extends BaseJsonObject {
        public String Street;
        public int Number;
    }
}

Here is how I initialize it:

ComplexJacksonObject cjo = new ComplexJacksonObject();
cjo.Count = 1;
cjo.Start = 2;
cjo.Strings = new ArrayList<String>();
cjo.Strings.add("One");
cjo.Strings.add("Two");

cjo.MyPerson = cjo.new Person();
cjo.MyPerson.Firstname = "Fi\",=[]{}rst";
cjo.MyPerson.Lastname = "Last";

cjo.MyPerson.Where = cjo.new Address();
cjo.MyPerson.Where.Street = "Street";
cjo.MyPerson.Where.Number = 15;

String result = cjo.toQueryString();        
// Strings=%5B%22One%22%2C%22Two%22%5D&MyPerson=%7BFirstname%3A"Fi%5C%5C%22%2C%3D%5B%5D%7B%7Drst"%2CLastname%3A%22Last%22%2CWhere%3A%7BStreet%3A%22Street%22%2CNumber%3A15%7D%7D&Start=2&Count=1

And finally the method that makes this happen:

public String toQueryString() {
    StringBuilder sb = new StringBuilder();
    for (Field field : this.getClass().getDeclaredFields()) {
        if (sb.length() > 0) {
            sb.append("&");
        }

        Class cls = field.getType().getSuperclass();
        // serializing my complex objects - they all inherit from BaseJsonObject class
        if (cls != null && cls.equals(BaseJsonObject.class)) {
            BaseJsonObject bjo = (BaseJsonObject) getFieldValue(field);
            String str = toJson(bjo, true);
            sb.append(field.getName()).append("=").append(Uri.encode(str));
        } 
        // serializing lists, they are all List<T>
        else if (field.getType().equals(List.class)) {
            List bjo = (List) getFieldValue(field);
            String val = toJson(bjo, false);
            sb.append(field.getName()).append("=").append(Uri.encode(val));
        } 
        // serializing simple fields
        else {
            Object bjo = getFieldValue(field);
            String val = toJson(bjo, false).replaceAll("^\"|\"$", "");
            sb.append(field.getName()).append("=").append(Uri.encode(val));
        }
    }

    return sb.toString();
}

private Object getFieldValue(Field field) {
    try {
        return field.get(this);
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

    return null;
}

private static ObjectMapper generateMapper() {
    ObjectMapper om = new ObjectMapper();
    // om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    om.setDateFormat(new JacksonSimpleDateFormat());

    return om;
}

public String toJson() {
    try {
        return generateMapper().writeValueAsString(this);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
        return null;
    }
}

public String toJson(Object o, boolean noQuoteProperties) {
    try {
        ObjectMapper om = generateMapper();
        if (noQuoteProperties) {                
            om.configure(com.fasterxml.jackson.core.JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
            om.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);           
        }
        return om.writeValueAsString(o);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
        return null;
    }
}
nikib3ro
  • 20,366
  • 24
  • 120
  • 181