I'm using RetroFit and Simple XML Framework in Android to model a SOAP response that looks like this:
XML:
<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<BuslocationResponse
xmlns="AT_WEB">
<Version>1.0</Version>
<Responsecode>0</Responsecode>
<Input>
<Route>801</Route>
<Direction>N</Direction>
</Input>
<Vehicles>
<Vehicle>
<Route>801</Route>
<Direction>N</Direction>
<Updatetime>09:42 PM</Updatetime>
<Vehicleid>5007</Vehicleid>
<Block>801-06</Block>
<Adherance>-2</Adherance>
<Adhchange>S</Adhchange>
<Reliable>Y</Reliable>
<Offroute>N</Offroute>
<Stopped>N</Stopped>
<Inservice>Y</Inservice>
<Speed>20.61</Speed>
<Heading> 3</Heading>
<Routeid>44916</Routeid>
<Positions>
<Position>30.221222,-97.765007</Position>
<Position>30.218363,-97.766747</Position>
<Position>30.215282,-97.768715</Position>
<Position>30.212505,-97.770485</Position>
<Position>30.204943,-97.774765</Position>
<Position>30.204035,-97.775078</Position>
</Positions>
</Vehicle>
</Vehicles>
</BuslocationResponse>
</soap:Body>
</soap:Envelope>
Really, all I care about is the collection of vehicles. It seems like I could model just the BusLocationResponse and skip the soap envelope and body by declaring the
Java:
#Root(strict=false)
#Path("Envelope/Body/BuslocationResponse")
public class BusLocationResponse {
#Element(name="Responsecode")
public int responseCode;
#ElementList
#Path("Envelope/Body/BuslocationResponse/Vehicles")
public List<CapVehicle> vehicles;
}
This just yields the error:
org.simpleframework.xml.core.ValueRequiredException: Unable to satisfy
#org.simpleframework.xml.Element(data=false, name=Responsecode, required=true,
type=void) on field 'responseCode'
What am I misunderstanding here?
You can't use #Path on #Root-Element:
The Path annotation is used to specify an XML path where an XML element or attribute is located.
( Source )
Since you want nested data, from somewhere deep in the xml, there are two solutions:
Map the whole XML structure
Use a Converter that cut's mapping down to few classes and map just those
And here's what to do if you choose No. 2:
The Plan
A SOAPEnvelope class builds just the root-element (<soap:Envelope>...</soap:Envelope>) and holds the list of vehicles
A SOAPEnvelopeConverter implements a Converter for SOAPEnvelope - there the serialization is reduced to vehicles list only
A class Vehicle holds all the data of those elements (incl. a class Position for the <Position>...</Position> elements)
A class Vehicles maps the vehicles-tag only (= list of vehicle elements).
(Names have no convention)
The Implementation
I've written an implementation as a reference so you can see how my suggested solution works. Please add error checking etc. All data fields are handled as String's here, replace their types with proper ones. Only the vehicles list is deserialized, all other values are ignored. Constructors, getter / setter etc. are only shown as they are required for this example.
The deserialized vehicles list is stored into the envelope's object. This is not the best way and used for example only. Please write a better implementation here (eg. introduce a class for the soap body where you can manage contents).
Note: Some classes are implemented as inner classes - this is optional, code as you prefer.
Class SOAPEnvelope / Class SOAPEnvelopeConverter (inner)
#Root(name = "Envelope")
#Namespace(prefix = "soap")
// Set the converter that's used for serialization
#Convert(value = SOAPEnvelope.SOAPEnvelopeConverter.class)
public class SOAPEnvelope
{
// Keep the content of vehicles list here
private Vehicles vehicles;
public Vehicles getVehicles()
{
return vehicles;
}
protected void setVehicles(Vehicles vehicles)
{
this.vehicles = vehicles;
}
// The converter implementation for SOAPEnvelope
public static class SOAPEnvelopeConverter implements Converter<SOAPEnvelope>
{
#Override
public SOAPEnvelope read(InputNode node) throws Exception
{
SOAPEnvelope envelope = new SOAPEnvelope();
InputNode vehiclesNode = findVehiclesNode(node); // Search the Vehicles list element
if( vehiclesNode == null )
{
// This is bad - do something useful here
throw new Exception("No vehicles node!");
}
/*
* A default serializer is used to deserialize the full node. The
* returned object is set into the envelops's object, where you can
* get it through a get()-method.
*/
Serializer ser = new Persister();
envelope.setVehicles(ser.read(Vehicles.class, vehiclesNode));
return envelope;
}
#Override
public void write(OutputNode node, SOAPEnvelope value) throws Exception
{
// If you read (deserialize) only there's no need to implement this
throw new UnsupportedOperationException("Not supported yet.");
}
private InputNode findVehiclesNode(InputNode rootNode) throws Exception
{
InputNode body = rootNode.getNext("Body");
InputNode buslocationResponse = body.getNext("BuslocationResponse");
InputNode next;
while( ( next = buslocationResponse.getNext() ) != null )
{
if( next.getName().equals("Vehicles") == true )
{
return next;
}
}
return null;
}
}
}
Class Vehicles
#Root(name = "Vehicles")
public class Vehicles
{
// Maps the list of vehicles
#ElementList(name = "Vehicles", inline = true)
private List<Vehicle> vehicles;
}
Class Vehicle
#Root(name = "Vehicle")
public class Vehicle
{
// All values are of type String - please replace with proper types
#Element(name = "Route")
private String route;
#Element(name = "Direction")
private String direction;
#Element(name = "Updatetime")
private String updateTime;
#Element(name = "Vehicleid")
private String vehicleID;
#Element(name = "Block")
private String block;
#Element(name = "Adherance")
private String adherance;
#Element(name = "Adhchange")
private String adhchange;
#Element(name = "Reliable")
private String reliable;
#Element(name = "Offroute")
private String offroute;
#Element(name = "Stopped")
private String stopped;
#Element(name = "Inservice")
private String inservice;
#Element(name = "Speed")
private String speed;
#Element(name = "Heading")
private String heading;
#Element(name = "Routeid")
private String routeID;
#ElementList(name = "Positions")
private List<Position> postions;
// A class to map the position elements
#Root(name = "Position")
public static class Position
{
#Text()
private String position;
}
}
How to use
final String xml = ...
Serializer ser = new Persister(new AnnotationStrategy()); // Annotation strategy is set here!
SOAPEnvelope soapEnvelope = ser.read(SOAPEnvelope.class, new StringReader(xml));
Nothing special here - only AnnotationStrategy is required! The source (2nd parameter of ser.read() is set as your input comes. In this example, the soap xml comes from a string.
the simple way is use #path , example i want get Route from node Soap/Body/BuslocationResponse/Vehicles/Vehicle
Response.java
#Root(name = "soap:Envelope", strict = false)
public class Response {
#Element(name = "Route")
#Path("Body/BuslocationResponse/Vehicles/Vehicle")
private int route;
public int getRoute() {
return route;
}
}
Main.java
class Main {
public static void main(String args[]) {
String xml = "<soap:Envelope \n" +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
"xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n" +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" +
"soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\n" +
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
"<soap:Body>\n" +
" <BuslocationResponse \n" +
" xmlns=\"AT_WEB\">\n" +
" <Version>1.0</Version>\n" +
" <Responsecode>0</Responsecode>\n" +
" <Input>\n" +
" <Route>801</Route>\n" +
" <Direction>N</Direction>\n" +
" </Input>\n" +
" <Vehicles>\n" +
" <Vehicle>\n" +
" <Route>801</Route>\n" +
" <Direction>N</Direction>\n" +
" <Updatetime>09:42 PM</Updatetime>\n" +
" <Vehicleid>5007</Vehicleid>\n" +
" <Block>801-06</Block>\n" +
" <Adherance>-2</Adherance>\n" +
" <Adhchange>S</Adhchange>\n" +
" <Reliable>Y</Reliable>\n" +
" <Offroute>N</Offroute>\n" +
" <Stopped>N</Stopped>\n" +
" <Inservice>Y</Inservice>\n" +
" <Speed>20.61</Speed>\n" +
" <Heading> 3</Heading>\n" +
" <Routeid>44916</Routeid>\n" +
" <Positions>\n" +
" <Position>30.221222,-97.765007</Position>\n" +
" <Position>30.218363,-97.766747</Position>\n" +
" <Position>30.215282,-97.768715</Position>\n" +
" <Position>30.212505,-97.770485</Position>\n" +
" <Position>30.204943,-97.774765</Position>\n" +
" <Position>30.204035,-97.775078</Position>\n" +
" </Positions>\n" +
" </Vehicle>\n" +
" </Vehicles>\n" +
"</BuslocationResponse>\n" +
"</soap:Body>\n" +
"</soap:Envelope>";
try {
Serializer serializer = new Persister();
Response r = serializer.read(Response.class, xml);
System.out.println("route: " + r.getRoute());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Result:
/usr/lib/jvm/java-8-oracle/bin/java....
route: 801
Process finished with exit code 0
Related
I'm trying to parse following JSON string with one field inside. Unfortunatelly still getting exception:
InvalidTypeIdException: Could not resolve type id 'pin' as a subtype of `com.example.dto.AuthorizationRequest`: known type ids = [AuthorizationRequest]
Here is base class:
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)
#JsonSubTypes({
#JsonSubTypes.Type(value = AuthorizationRequest.class, name = "AuthorizationRequest")})
public interface IMessage {}
Derived class:
public class AuthorizationRequest implements IMessage {
#JsonProperty( value = "pin", required = true )
private String pin;
public String getPin() {
return pin;
}
public void setPin(String pin) {
this.pin = pin;
}
}
Test:
#Test
void test(){
String request =
"{\n"
+ " \"AuthorizationRequest\": {\n"
+ " \"pin\": \"1234\"\n"
+ " }\n"
+ "}";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
AuthorizationRequest authorizationRequest = null;
try {
authorizationRequest = objectMapper.readValue(request, AuthorizationRequest.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
What am I missing? I need to use request name as json root element.
You can delete objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true) line. The error is due to the fact that the line unwraps the initial object so escalating of one level at the intern of the json file obtaining the {"pin" : "1234"} json with the ""AuthorizationRequest" label. The JsonTypeInfo.As.WRAPPER_OBJECT annotation indicates to the jackson library to escalate again of one level the json file so obtaining the "1234" string labelled by "pin".The JsonTypeInfo.Id.NAME compares the "pin" string with the names of subclasses and because of it fails causing the issue and the error message.
Am trying to get data from 3 tables and every time I end up getting an error
Ljava.lang.Object; cannot be cast to .model.ISECO at
java.util.ArrayList.forEach
This are my entities
#Entity
public class IS01 {
private String IEA;
private String INUM;
private String ILINE;
private String I0103;
#Entity
public class ISOVER {
private String IEA;
private String ILINE;
private String INUM;
private String IRESULT;
private String ICON;
private String IBCON;
private String CASE;
private String RPTID
#Entity
public class POSTCO {
private String CEA;
private String CNUM;
private String CLINE;
private String PSCONTACT;
And this is my Repository
public interface LineSummary extends CrudRepository<ISOVER , String> {
#Query("select c.ILINE , c.IRESULT,e.PSCONTACT, \n" +
"c.ICON,c.IBCON, c.RPTID, c.CASE, d.i0103 as age\n" +
"FROM ISOVER c \n" +
"inner join IS01 d \n" +
"on c.IEA = d.IEA and c.INUM = d.INUM and c.ILINE = d.ILINE\n" +
"inner join POSTCO e on d.IEA = e.CEA and d.INUM = e.CNUM and d.ILINE = e.CLINE\n" +
"where c.CASE like %?1%")
Iterable<ISOVER> findEntriesByUserId(#Param("Case") String Case);
And this is my service
public ResponseEntity<Map<String, Object>> retrieveLineListingSQL(String Case){
Iterable <ISOVER > stud = lineSummary.findEntriesByUserId(Case);
Map<String, Object> parents = new HashMap<>();
parents.put("totalMembers", 9);
parents.put("questionaryinfo", new ArrayList<HashMap<String,Object>>());
ArrayList<HashMap<String,Object>> listings = (ArrayList<HashMap<String,Object>>) parents.get("questionaryinfo");
if (stud != null) {
stud.forEach(d -> {
HashMap<String,Object> entry = new HashMap<>();
entry.put("adultquestionary","Yes");
entry.put("caseNumber", d.getCASE());
listings.add(entry);
});
}
parents.put("DMStatus", "No review");
parents.put("ages", new HashMap<String, Object>());
return ResponseEntity.ok().body(parents);
}
How can I return the results from the query and map them accordingly?
I believe this is your culprit:
if (stud != null) {
stud.forEach(d -> {
HashMap<String, Object> entry = new HashMap<>(); // < -- here
entry.put("adultquestionary","Yes");
entry.put("caseNumber", d.getCASE());
listings.add(entry);
});
}
Have your tried using *.model.ISECO instead of java.lang.Object? Does that work, any particular limitation?
Additionally, you could refactor you code to something way more simple, if you follow the same explanation provided in here: How to make nested JSON response with Array from a Stored procedure
Create a response model that outputs the format you expect as response.
There is no need for you to do all that collections handling one-by-one. The representation of an object in JSON is a MAP, basically let the
Jackson JSON library do all that work for you.
I am trying to add a unique validation on a field while adding a product on Broadleaf. Currently we have added a 'SKU' field while adding product from admin screen. I have used the following annotation to validate:
#AdminPresentationMergeOverride(name = "userSku", mergeEntries = #AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.VALIDATIONCONFIGURATIONS, validationConfigurations = {
#ValidationConfiguration(validationImplementation = "blUniqueValueValidator", configurationItems = {
#ConfigurationItem(itemName = "otherField", itemValue = "userSku") }) })
It works perfect when we try to add a new product.
But the problem is, If I try to update any product to change any field, it gives the same validation error
Looks like that doesn't work quite right, can you open an issue in https://github.com/BroadleafCommerce/Issues?
You can also write your own uniqueness validates that does not run into the same ID problem like so:
#Component
public class MyUniqueValueValidator implements PropertyValidator {
protected static final Log LOG = LogFactory.getLog(UniqueValueValidator.class);
#Override
public PropertyValidationResult validate(Entity entity,
Serializable instance,
Map<String, FieldMetadata> entityFieldMetadata,
Map<String, String> validationConfiguration,
BasicFieldMetadata propertyMetadata,
String propertyName,
String value) {
String instanceClassName = instance.getClass().getName();
DynamicEntityDao dynamicEntityDao = getDynamicEntityDao(instanceClassName);
List<Long> responseIds = dynamicEntityDao.readOtherEntitiesWithPropertyValue(instance, propertyName, value);
String message = validationConfiguration.get(ConfigurationItem.ERROR_MESSAGE);
if (message == null) {
message = entity.getType()[0] + " with this value for attribute " +
propertyName + " already exists. This attribute's value must be unique.";
}
boolean onlyInCurrentEntity = CollectionUtils.isEmpty(responseIds)
|| (responseIds.size() == 1 && responseIds.get(0).equals(getDynamicEntityDao(instanceClassName).getIdentifier(instance)));
return new PropertyValidationResult(onlyInCurrentEntity, message);
}
protected DynamicEntityDao getDynamicEntityDao(String className) {
return PersistenceManagerFactory.getPersistenceManager(className).getDynamicEntityDao();
}
}
And then use the validator by passing in the bean ID to the validationImplementation:
#AdminPresentationMergeOverride(name = "userSku", mergeEntries = #AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.VALIDATIONCONFIGURATIONS, validationConfigurations = {
#ValidationConfiguration(validationImplementation = "myUniqueValidator", configurationItems = {
#ConfigurationItem(itemName = "otherField", itemValue = "userSku") }) })
This is my XML file :-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<logExtract>
<configuration>
<splunk>
<splunkHost>localhost</splunkHost>
<userName>abcd</userName>
<password>1234</password>
<port>8214</port>
</splunk>
<tsdb>
<tsdbHost>localhsot</tsdbHost>
<port>4242</port>
</tsdb>
</configuration>
<dataPart>
<ingestion id="abc">
<tsdbElements>
<metricname>linecount0</metricname>
<tags>splunk_server0</tags>
</tsdbElements>
<splunkQuery>
<Query>index=_internal source=*/splunkd_access.log |head 0000</Query>
</splunkQuery>
</ingestion>
<ingestion id="xyz">
<tsdbElements>
<metricname>linecount</metricname>
<tags>splunk_server</tags>
</tsdbElements>
<splunkQuery>
<query>index=_internal source=*/splunkd_access.log |head 1000</query>
</splunkQuery>
</ingestion>
<ingestion id="def">
<tsdbElements>
<metricname>linecount2</metricname>
<tags>splunk_server2</tags>
</tsdbElements>
<splunkQuery>
<query>index=_internal source=*/splunkd_access.log |head 2000</query>
</splunkQuery>
</ingestion>
</dataPart>
</logExtract>
I have used JAXB and created POJO class structure for it.
For Ingestion element this is my POJO class structure.
private String id;
private List<TsdbElements> TsdbElements;
private List<SplunkQuery> SplunkQuery;
#XmlAttribute
public String getId ()
{
return id;
}
public void setId (String id)
{
this.id = id;
}
#XmlElement
public List<TsdbElements> getTsdbElements ()
{
return TsdbElements;
}
public void setTsdbElements (List<TsdbElements> TsdbElements)
{
this.TsdbElements = TsdbElements;
}
#XmlElement
public List<SplunkQuery> getSplunkQuery ()
{
return SplunkQuery;
}
public void setSplunkQuery (List<SplunkQuery> SplunkQuery)
{
this.SplunkQuery = SplunkQuery;
}
#Override
public String toString()
{
return "ClassPojo [id = "+id+", TsdbElements = "+TsdbElements+", SplunkQuery = "+SplunkQuery+"]";
}
Here is the Problem :-
When I try to extract Objects of ingestion I get error
(java.util.ArrayList cannot be cast to com.jaxb.xmlfile.Ingestio) java.lang.ClassCastException
at line below comment.
String fileName = "Query.xml";
File file = new File(fileName);
//JAXB Parsing - Unmarshling XML File
JAXBContext jaxbContext = JAXBContext.newInstance(XMLData.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
LogExtract logExtract = (LogExtract) jaxbUnmarshaller.unmarshal(file);
Configuration config = logExtract.getConfiguration();
Splunk spluknData = config.getSplunk();
Tsdb tsdbData = config.getTsdb();
DataPart dataPart = logExtract.getDataPart();
List<Ingestion> ingestionData = dataPart.getIngestion();
//Here I get Error
List<TsdbElements> tsdbElementsData = ((Ingestion) ingestionData).getTsdbElements();
//Here I get Error
List<SplunkQuery> splunkQueryData = ((Ingestion) ingestionData).getSplunkQuery();
System.out.println(spluknData.getSplunkHost() + " " + spluknData.getUserName() + " " + spluknData.getPassword() + " " +spluknData.getPort());
System.out.println(tsdbData.getTsdbHost() + " " + tsdbData.getPort());
for (SplunkQuery splunkQuery : splunkQueryData) {
System.out.println(splunkQuery.getQuery());
}
for (TsdbElements tsdbElements : tsdbElementsData) {
System.out.println(tsdbElements.getMetricname() + " " + tsdbElements.getTags());
}
So what am I missing?
EDIT:- (After answer given by #Sanj)
How to save tsdbElement data using for loop and then access them again out of for loop? Any Idea? Because its only saving last XML data, not all of them
List<Ingestion> ingestionData = dataPart.getIngestion();
//Here I get Error
List<TsdbElements> tsdbElementsData = ((Ingestion) ingestionData).getTsdbElements();
The error is stating that ingestionData is a type List, and you are trying to cast it to the Ingestion class.
Looking at your XML, you have a list of of these elements
<ingestion id="abc">
<tsdbElements>
<metricname>linecount0</metricname>
<tags>splunk_server0</tags>
</tsdbElements>
<splunkQuery>
<Query>index=_internal source=*/splunkd_access.log |head 0000</Query>
</splunkQuery>
</ingestion>
So you just need to iterate the list ingestionData to get the tsdbElements. Something like
// instantiate the tsdbElementsData list
List<TsdbElements> tsdbElementsData = new ArrayList<>(TsdbElements)
for (Ingestion ingestion: ingestionData)
{
// get the elements
tsdbElements = ingestion.getTsdbElements();
// do some with the elements, e,g add to a another list
tsdbElementsData.add(tsdbElements);
}
To iterate through the tsdbElementsData list, it is just another loop
for (TsdbElements tsdbElements: tsdbElementsData)
{
// ... do something with tsdbElements
}
Note that the foreach loop above, is the same a writing
for (int i = 0; i < tsdbElementsData.size(); i++)
{
TsdbElements tsdbElements = tsdbElementsData.get(i);
// ... do something with tsdbElements
}
I recive object like that
{
"data": "some data",
"social": {
"twitter": "id"
}
}
This is easly parsed using next classes
public class SocialLinks {
#Expose
private String data;
#Expose
private Social social;
}
public class Social {
#Expose
private String twitter;
}
Unfortunatly due to some issues, if social is empty it is returened as array
{
"data": "some data",
"social": [
]
}
How can I parse it with gson? (I am not a developer of server side and cannot affect responce meassages)
You can do that using these classes.
SocialLinks.java
public class SocialLinks {
private String data;
private Social social;
// Getters && Setters
}
Social.java:
public class Social {
private String twitter;
// Getters & Setters
}
And here is your main method
public class GsonApp {
private static final String TEST_JSON = "{\n" +
" \"data\": \"some data\",\n" +
" \"social\": {\n" +
" \"twitter\": \"id\"\n" +
" }\n" +
"}";
public static void main(String[] args) throws Exception {
final Gson gson = new GsonBuilder().create();
// Read Example
final SocialLinks socialLinks = gson.fromJson(TEST_JSON, SocialLinks.class);
System.out.println(gson.toJson(socialLinks));
// Write with null Social
final SocialLinks socialLinks1 = new SocialLinks();
socialLinks1.setData("MyData");
System.out.println(gson.toJson(socialLinks1));
// Write with empty Social (social.twitter is null)
final SocialLinks socialLinks2 = new SocialLinks();
socialLinks2.setData("MyData");
socialLinks2.setSocial(new Social());
System.out.println(gson.toJson(socialLinks2));
// Write with full Social
final SocialLinks socialLinks3 = new SocialLinks();
socialLinks3.setData("MyData");
socialLinks3.setSocial(new Social());
socialLinks3.getSocial().setTwitter("ID");
System.out.println(gson.toJson(socialLinks3));
}
}
This will output
{"data":"some data","social":{"twitter":"id"}}
{"data":"MyData"}
{"data":"MyData","social":{}}
{"data":"MyData","social":{"twitter":"ID"}}
Update
If you data type changes depending on your application state you may want to create Map object instead of DTO. Here is an example
private static final String TEST_JSON_2 = "{\n" +
" \"data\": \"some data\",\n" +
" \"social\": [\n" +
" ]\n" +
"}";
...
Type type = new TypeToken<Map<String, Object>>(){}.getType();
final Map<String, Object> socialLinks4 = gson.fromJson(TEST_JSON_2, type);
System.out.println(socialLinks4);
final Map<String, Object> socialLinks5 = gson.fromJson(TEST_JSON, type);
System.out.println(socialLinks5);
This will output
{data=some data, social=[]}
{data=some data, social={twitter=id}}