Arvind
Arvind

Reputation: 6474

how to create data members for bean, to be used to parse JSON data using Jackson parser (or similar)

I read some tutorials that say that a simple bean should be defined with getter and setter methods representing the JSON data in a systematic way.

Given below is the data that I will have to parse- pls help me by defining the bean for this type of situation (where one node contains many nodes, each of which contain distinct sub nodes)

I require from_title, from_url, to_title and to_url for approx 2500 records out of the JSON output.

----------------JSON DATA SAMPLE----------------------------

{
"NOTICE" : [...terms of use appear here...]
"links" : [
{
"anchor_has_img" : false,
"anchor_text" : "",
"exclude" : false,
"follow" : true,
"from_host_rank10" : 3.48817529303454,
"from_ip" : "66.96.130.51",
"from_ip_geo" : {
"city" : "Burlington",
"country_code" : "US",
"isp_org" : "The Endurance International Group",
"latitude" : "42.5051",
"longitude" : "-71.2047",
"state" : "MA",
"zip" : "01803"
},
"from_pubdate" : 1287298800,
"from_rank10" : 4.1e-05,
"from_title" : "Antique Links",
"from_url" : "http://www.100tonsofstuff.com/links-antiquesresources.
htm",
"to_http_status" : 301,
"to_rank10" : 8.97651069961698,
"to_redir" : "http://geocities.yahoo.com/",
"to_title" : "Get a web site with easy-to-use site building tools -
Marengo Inn - Los Angeles Hotel - Yahoo - GeoCities",
"to_url" : "http://www.geocities.com/"
},
[...2499 entries skipped for this example...]
"nlinks_avail" : 2500,
"nlinks_est_total" : 11630.5708745538,
"to_rank10" : 8.97651069961698
}

Upvotes: 0

Views: 973

Answers (2)

StaxMan
StaxMan

Reputation: 116620

Depending on how many types with "non-Java" names you have, you can either implement approach Bruce recommends (which scales nicely for large number of types); or just 'manually' rename things, using @JsonProperty annotations to map JSON values into Java properties. Like so:

public class Bean {
   @JsonProperty("NOTICE") public List<String> noticeTerms;
   public List<LinkObject> links;
}
public class LinkObject {
   @JsonProperty(""from_pubdate") public Date fromPublicationDate;
   // ...
}

Upvotes: 0

Programmer Bruce
Programmer Bruce

Reputation: 67003

Here's what I'd do.

import java.io.File;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.net.URI;
import java.util.Date;
import java.util.List;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.MapperConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.PropertyNamingStrategy;
import org.codehaus.jackson.map.introspect.AnnotatedField;
import org.codehaus.jackson.map.introspect.AnnotatedMethod;

public class Foo
{
  public static void main(String[] args) throws Exception
  {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setPropertyNamingStrategy(new CamelCaseToLowerCaseWithUnderscoresNamingStrategy());
    mapper.setVisibilityChecker(mapper.getVisibilityChecker().withFieldVisibility(Visibility.ANY));

    Response response = mapper.readValue(new File("input.json"), Response.class);
    String json = mapper.writeValueAsString(response);
    System.out.println(json);

    // Check for correctness
    JsonNode originalInput = mapper.readTree(new FileInputStream("input.json")); // next release of Jackson will have readTree that just takes a File object reference
    JsonNode generatedOutput = mapper.readTree(json);
    System.out.println("Are they the same?");
    System.out.println(originalInput.equals(generatedOutput) ? "yes" : "no");
  }
}

class Response
{
  @JsonProperty("NOTICE")
  String notice;
  List<Link> links;
  int nlinksAvail;
  BigDecimal nlinksEstTotal;
  BigDecimal toRank10;
}

class Link
{
  boolean anchorHasImg;
  String anchorText;
  boolean exclude;
  boolean follow;
  BigDecimal fromHostRank10;
  String fromIp;
  Geo fromIpGeo;
  Date fromPubdate;
  BigDecimal fromRank10;
  String fromTitle;
  URI fromUrl;
  int toHttpStatus;
  BigDecimal toRank10;
  URI toRedir;
  String toTitle;
  URI toUrl;
}

class Geo
{
  String city;
  String countryCode;
  String ispOrg;
  String latitude;
  String longitude;
  State state;
  String zip;
}

enum State
{
  MA, MN, NJ
}

class CamelCaseToLowerCaseWithUnderscoresNamingStrategy extends PropertyNamingStrategy
{
  @Override  
  public String nameForGetterMethod(MapperConfig<?> config,  
      AnnotatedMethod method, String defaultName)  
  {  
    return translate(defaultName);  
  }  

  @Override  
  public String nameForSetterMethod(MapperConfig<?> config,  
      AnnotatedMethod method, String defaultName)  
  {  
    return translate(defaultName);  
  }  

  @Override  
  public String nameForField(MapperConfig<?> config,  
      AnnotatedField field, String defaultName)  
  {  
    return translate(defaultName);  
  }  

  private String translate(String defaultName)  
  {  
    char[] nameChars = defaultName.toCharArray();  
    StringBuilder nameTranslated =  
        new StringBuilder(nameChars.length * 2);  
    for (char c : nameChars)  
    {  
      if (Character.isUpperCase(c))  
      {  
        nameTranslated.append("_");  
        c = Character.toLowerCase(c);  
      }  
      nameTranslated.append(c);  
    }  
    return nameTranslated.toString();  
  }
}

I might also figure out an enum for the country codes.

Of course, the fields would actually be private.

A more complete PropertyNamingStrategy is available at http://jira.codehaus.org/browse/JACKSON-598.

Upvotes: 2

Related Questions