Using Custom To_JSON Method in Nested Objects

Custom Json Serializer for deep nested objects

Your "goal" JSON is tricky to handle because the treatment of the SubDataMappers list is different depending on whether the children have a non-null DataMapperProperty or a non-empty list of SubDataMappers. In the former case, you want it rendered as an object containing one property per child DataMapper; in the latter, as an array of objects containing one DataMapper each. Also, I see you are using the Name property of the DataMapper as a key in the JSON rather than as the value of a well-known property. Given these two constraints, I think the best plan of attack is to make a JsonConverter that operates on a list of DataMappers rather than a single instance. Otherwise, the converter code is going to get pretty messy. If that is acceptable, then the following converter should give you what you want:

public class DataMapperListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<DataMapper>);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<DataMapper> list = (List<DataMapper>)value;
if (list.Any(dm => dm.DataMapperProperty != null))
{
JObject obj = new JObject(list.Select(dm =>
{
JToken val;
if (dm.DataMapperProperty != null)
val = JToken.FromObject(dm.DataMapperProperty, serializer);
else
val = JToken.FromObject(dm.SubDataMappers, serializer);
return new JProperty(dm.Name, val);
}));
obj.WriteTo(writer);
}
else
{
serializer.Serialize(writer,
list.Select(dm => new Dictionary<string, List<DataMapper>>
{
{ dm.Name, dm.SubDataMappers }
}));
}
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
return token.Children<JProperty>()
.Select(jp =>
{
DataMapper mapper = new DataMapper { Name = jp.Name };
JToken val = jp.Value;
if (val["data-type"] != null)
mapper.DataMapperProperty = jp.Value.ToObject<DataMapperProperty>(serializer);
else
mapper.SubDataMappers = jp.Value.ToObject<List<DataMapper>>(serializer);
return mapper;
})
.ToList();
}
else if (token.Type == JTokenType.Array)
{
return token.Children<JObject>()
.SelectMany(jo => jo.Properties())
.Select(jp => new DataMapper
{
Name = jp.Name,
SubDataMappers = jp.Value.ToObject<List<DataMapper>>(serializer)
})
.ToList();
}
else
{
throw new JsonException("Unexpected token type: " + token.Type.ToString());
}
}
}

Assumptions:

  • You will never be serializing a single DataMapper by itself; it will always be contained in a list.
  • DataMappers can be nested to an arbitrary depth.
  • A DataMapper will always have a non-null Name, which is unique at each level.
  • A DataMapper will never have both a non-null DataMapperProperty and a non-empty list of SubDataMappers.
  • A DataMapperProperty will always have a non-null DataType.
  • A DataMapper will never have a Name of data-type.

If the last four assumptions do not hold true, then this JSON format will not work for what you are trying to do, and you will need to rethink.

To use the converter, you will need to add it to your serializer settings as shown below. Use the settings both when you serialize and deserialize. Remove the [JsonConverter] attribute from the DataMapper class.

var settings = new JsonSerializerSettings()
{
Converters = new List<JsonConverter> { new DataMapperListConverter() },
Formatting = Formatting.Indented
};

Here is a round-trip demo: https://dotnetfiddle.net/8KycXB

JS Creating a custom json by a nested json

Please replace your code with below one, it will work straight away.
Key will be "state-1", "state-2" instead of "0", "1"

function transform(){  
let items = [
{
"carId":328288,
"firstName":"yathindra",
"lastName":"rawya",
"list":[
{
"id":182396,
"isAvail":false,
"stateId":288,
"state":"Awesome"
},
{
"id":182396,
"isAvail":false,
"stateId":678,
"state":"Cool1"
}
],
},
{
"carId":3282488,
"firstName":"yathindraR",
"lastName":"K",
"list":[
{
"id":18232396,
"isAvail":false,
"stateId":22388,
"state":"Awesome"
},
{
"id":182356796,
"isAvail":false,
"stateId":45678,
"state":"Cool"
}
],
}
]

let customList = [];

for(let i=0;i<items.length;i++){

let temp = {};
for(let j=0;j<items[i].list.length;j++){
temp["state-"+(j+1)] = items[i].list[j].state;
}

customList.push({
fname: items[i].firstName,
lname: items[i].lastName,
...temp
})
}

console.log(JSON.stringify(customList))
}

transform();

Gson - Serialize Nested Object as Attributes

Update I looked into GsonBuilder and yes you can do it with custom serialization. You need to override serialize method of JsonSerializer<type>

Just define a class as below. here only 2 properties are added.

public class FooSerialize implements JsonSerializer<foo> {

@Override
public JsonElement serialize(foo obj, Type foo, JsonSerializationContext context) {

JsonObject object = new JsonObject();
String otherValue = obj.b.other;
object.addProperty("other", otherValue );
object.addProperty("text", obj.text);
return object;
}
}

Create gson object as below.

Gson gson = new GsonBuilder().registerTypeAdapter(foo.class, new FooSerialize()).setPrettyPrinting().create();

Just convert to Json

 gson.toJson(fooObject);

Voila! lmk if it works for you. I tested on my system it worked. Forget about to string override it gets called for Json to Obj conversion. This is only serialization you need to handle deserialize to object as well. Look for online resources to get an idea on similar line.

Alternate solution would be define dummy pojos only for JSON conversion purposes. While sending use setters to assign values to pojo object and use gson on pojo vice versa or above solution to have custom serialize and deserialize for class you need.

Deserialization with nested object and in-between step

I recommend:

  1. a dedicated Dto; or
  2. [JsonIgnore] on License and an auxiliary field;

For example:

public class LicenseFile
{
[JsonIgnore]
public License License { get; set; }

public string LicenseJson => JsonSerializer.Serialize(License); // Add base64 encoding if you wish

public byte[] Signature { get; set; }
}

or

public class LicenseFileDto
{
public LicenseFileDto(LicenseFile license) {
License = JsonSerializer.Serialize(license.License);
Signature = license.Signature;
}
public string License { get; }
public byte[] Signature { get; }
}

Test

var l = new LicenseFile
{
License = new License(name: "X Y", validUntil: new DateTime(2021, 04, 22)),
Signature = new byte[] { 1, 2, 3 }
};

var json = JsonSerializer.Serialize(l,
options: new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

var dtoJson = JsonSerializer.Serialize(new LicenseFileDto(l),
options: new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(dtoJson);

// Documentation at:
// https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-encoding#serialize-all-characters
// says:
// Use the unsafe encoder only when it's known that the client will be interpreting the resulting payload as UTF-8 encoded JSON.
var prettyJson = JsonSerializer.Serialize(new LicenseFileDto(l),
options: new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
Console.WriteLine(prettyJson);
{
"LicenseJson": "{\u0022Name\u0022:\u0022X Y\u0022,\u0022ValidUntil\u0022:\u00222021-04-22T00:00:00\u0022}",
"Signature": "AQID"
}
{
"License": "{\u0022Name\u0022:\u0022X Y\u0022,\u0022ValidUntil\u0022:\u00222021-04-22T00:00:00\u0022}",
"Signature": "AQID"
}
{
"License": "{\"Name\":\"X Y\",\"ValidUntil\":\"2021-04-22T00:00:00\"}",
"Signature": "AQID"
}

Serialize flat object to nested JSON structure

Where it says // how to write the nested structure, have you tried something like

   jgen.writeObjectFieldStart("Other")
jgen.writeNumberField("Nested.BusinessName", value.prop2)
jgen.writeEndObject()

Django serialise nested object into json

ser = ForumSubSectionSerializer(qs, many=True) already serializes the data for you. All you need to do is: get the data from it with ser.data or you may access the data directly from the class ForumSubSectionSerializer(qs, many=True).data.

def ajax_edit_forum_subsection(request):
qs = ForumSubSection.objects.all().order_by('pk')
ser = ForumSubSectionSerializer(qs, many=True).data # <- data here
return JsonResponse(ser, safe=False)

Notice the many=Trye parameter. If a representation should be a list of items, you should pass the many=True flag to the serializer.



Related Topics



Leave a reply



Submit