JSON.Net references become null - serialization

I'm serializing my game state with JSON.Net, but weirdly some references are becoming null. They seem to serialize fine (the $id and $ref seem to be properly set when checking the output json), but the deserialized objectes contain null references where they shouldn't.
My state is as follows: I have a planet, which contains a list of tiles. Each tile knows it neighbours (again a list of tiles).
Here's a little piece of the json, the initial serialization: (most important part is the last line, where neighbours has uses a ref number)
"Tiles": {
"$id": "3",
"$values": [
{
"$id": "4",
"$type": "WarSystems.Tile, Assembly-CSharp",
"_type": 0,
"ID": "161d8ca1-f086-49eb-94ba-0b5b3bbb0921",
"Position": {
"x": 0.0,
"y": 0.0,
"z": -1.71737635
},
"Normal": {
"x": 0.0,
"y": 0.0,
"z": -1.0
},
"Neighbours": [
{
"$id": "5",
"$type": "WarSystems.Tile, Assembly-CSharp",
"_type": 2,
"ID": "f34a2bb1-4a10-49db-b2d5-7980ccc55760",
"Position": {
"x": 0.2789981,
"y": -0.8586796,
"z": -1.460893
},
"Normal": {
"x": 0.1624527,
"y": -0.4999933,
"z": -0.850656152
},
"Neighbours": [
{
"$ref": "4"
},
"Tiles" is the list of tiles in the planet. As you can see, the first tile has a neighbour, and their neighbour has the first tile as his neighbour. Serializing goes fine, all there neighbours are set properly.
However, when I deserialize this, and then reserialize that, I get the following: (now notice that neighbours has a null)
"Tiles": {
"$id": "3",
"$values": [
{
"$id": "4",
"$type": "WarSystems.Tile, Assembly-CSharp",
"_type": 0,
"ID": "161d8ca1-f086-49eb-94ba-0b5b3bbb0921",
"Position": {
"x": 0.0,
"y": 0.0,
"z": -1.71737635
},
"Normal": {
"x": 0.0,
"y": 0.0,
"z": -1.0
},
"Neighbours": [
{
"$id": "5",
"$type": "WarSystems.Tile, Assembly-CSharp",
"_type": 2,
"ID": "f34a2bb1-4a10-49db-b2d5-7980ccc55760",
"Position": {
"x": 0.2789981,
"y": -0.8586796,
"z": -1.460893
},
"Normal": {
"x": 0.1624527,
"y": -0.4999933,
"z": -0.850656152
},
"Neighbours": [
null,
As you can see, the first neighbour is now null. (I also checked the actual deserialized object, it also has the null reference, so it seems the deserializing goes wrong. Of course there is a lot more, but this part shows what's going wrong and the whole json would be a bit much to post.)
Lastly, this is how I (de)serialize:
private static string SerializeState(State state)
{
return JsonConvert.SerializeObject(state, SerializerSettings);
}
private static State DeserializeState(string serialized)
{
return JsonConvert.DeserializeObject<State>(serialized, SerializerSettings);
}
With the SerializerSettings shared between them:
private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
PreserveReferencesHandling = PreserveReferencesHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Converters = new List<JsonConverter>() { new Vector3Converter() },
Formatting = Formatting.Indented
};
(Vector3Converter is used to serialize Unity's Vector3. It simply serializes it as an object with 3 field: x, y, and z. As the Position property in the tiles.)
Does anyone know what's going wrong, and how to fix it?
Thanks!
Edit, the Planet and Tile class:
public class Planet
{
public Vector3 Position { get; set; }
public List<Tile> Tiles { get; set; }
public Planet(Vector3 pos, List<Tile> tiles)
{
Position = pos;
Tiles = tiles;
}
}
public class Tile
{
public Guid ID { get; set; }
[JsonRequired]
private TileType _type;
[JsonIgnore]
public TileType Type
{
get { return _type; }
set
{
if (_type != value)
{
_type = value;
if (TypeChanged != null) TypeChanged(_type);
}
}
}
public Vector3 Position { get; set; }
public Vector3 Normal { get; set; }
private List<Tile> _neighbours = new List<Tile>(6); //6 since we use a hex grid
public List<Tile> Neighbours { get { return _neighbours; } set { _neighbours = value; } }
// Events
public event System.Action<TileType> TypeChanged;
public Tile(TileType type, Vector3 pos, Vector3 normal)
{
ID = Guid.NewGuid();
Position = pos;
Normal = normal;
}
public void AddNeighbour(Tile neighbour)
{
_neighbours.Add(neighbour);
}
}

I've solved it. Once I added an empty constructor in my Tile class everything works as intended.
public Tile() { } //for deserialization

Related

How to returning only as string column in json response object from eloquent relationship using eager loading

I'm trying to figure out how to eager load data as a string instead of array object from a related table.
I have 3 models and here are the relations
Product.php
class Product extends Model
{
public function ProductTag()
{
return $this->hasMany(ProductTag::class);
}
public function Category()
{
return $this->belongsTo(Category::class);
}
}
ProductTag.php
class ProductTag extends Model
{
public function Product()
{
return $this->belongsTo(Product::class);
}
}
Category.php
class Category extends Model
{
public function Product()
{
return $this->hasMany(Product::class);
}
}
I've tried doing it like so:
public function tag(){
return $this->hasMany(ProductTag::class)
->selectRaw('GROUP_CONCAT(tag) as tag,id')
->groupBy('id');
}
public static function list(){
$result = Category::with(['Product'=>function($q){
$q->with(['tag'=>function($q1){
$q1->first();
}]);
}])->get();
}
Here is the reponse:
{
"data": {
"categories": [
{
"id": 1,
"category": "test 1",
"product": [
{
"id": 46,
"name": "test prod 1",
"tag": []
},
{...}
]
},
{
"id": 2,
"category": "test 2",
"product": [
{
"id": 45,
"name": "test prod 2",
"tag": [
{
"product_tag": "Test1, test12, test123"
}
]
},
{...}
]
},
{
"id": 3,
"category": "test 3",
"product": []
}
]
}
}
The Response is as expected except tag array so, instead of an array named "tag" can I get "product_tag" within the "product" array
"product": [
{
"id": 45,
"name": "test prod 2",
"tag": [
{
"product_tag": "Test1, test12, test123"
}
]
}
]
Here is what I want it to look like:
"product": [
{
"id": 45,
"name": "test prod 2",
"product_tag": "Test1, test12, test123"
}
]
Is it possible and any smart way of doing this in Laravel using Eloquent?
Simple :)
Btw, if you can - rename product_tag in response to tag_line or same - it's not right way to take same name for relation and mutator.
class Product extends Model
{
public function getTagLineAttribute()
{
//tag - is "name" field in ProductTag
return $this->ProductTag->pluck('tag')->implode(',');
}
public function ProductTag()
{
return $this->hasMany(ProductTag::class);
}
public function Category()
{
return $this->belongsTo(Category::class);
}
}
//eager loading with tags
Product::with('ProductTag')->find(..)->tagLine;

How to return only queried items from table?

I have an H2 database ran in a spring-boot application with the following table of data.
I am able to send get requests from postman to get the entire output of the table.
[
{
"id": 1,
"category": "AI",
"title": "AI 2021",
"content": "Potential to take over the world"
},
{
"id": 33,
"category": "Sports",
"title": "Super Bowl",
"content": "What a boring game"
},
{
"id": 65,
"category": "Sports",
"title": "Cleveland Browns",
"content": "Future Cahmpions"
},
{
"id": 66,
"category": "Sports",
"title": "Cleveland Browns",
"content": "Future Cahmpions"
},
{
"id": 67,
"category": "Sports",
"title": "Cleveland Browns",
"content": "Future Cahmpions"
},
{
"id": 68,
"category": "Sports",
"title": "",
"content": ""
},
{
"id": 69,
"category": "Sports",
"title": null,
"content": null
},
{
"id": 70,
"category": "Sports",
"title": "aasdf",
"content": "asdfasdav"
},
{
"id": 97,
"category": "Sports",
"title": "Fitness Life Hack",
"content": "Get Ripped"
},
{
"id": 98,
"category": "Sports",
"title": "Spring FCS Games",
"content": "Wish this happened every year"
},
{
"id": 99,
"category": "Sports",
"title": "Football",
"content": "Sometimes called soccer"
},
{
"id": 100,
"category": "Sports",
"title": "Rocket League",
"content": "That's a real sport right?"
},
{
"id": 101,
"category": "Education",
"title": "Engineering",
"content": "Get Smart"
},
{
"id": 102,
"category": "Education",
"title": "Math",
"content": "2+2=4"
}
]
When I try to add a query string at the end for example http://localhost:8080/Posts/?category=Sports I still get the same output.
I've tried querying for id and other variables and nothing seems to make a difference I always get the entire table as output. I am trying to get it to only return the values where category equals sports. I know findAll() is not the right method for this. I have tried findCategory() from reading the JPQL docs but I was not able to get that to compile. Is there a method I am missing that I should be using?
PostRepository.java
package com.open.forum;
import org.springframework.data.jpa.repository.JpaRepository;
interface PostRepository extends JpaRepository<Post, Long> {
}
PostController.java
package com.open.forum;
import java.util.List;
import java.util.Optional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
class PostController {
private final PostRepository repository;
PostController(PostRepository repository) {
this.repository = repository;
}
// Aggregate root
// tag::get-aggregate-root[]
#GetMapping("/Posts")
#ResponseBody
List<Post> all(#RequestParam String category) {
return repository.findAll();
}
// end::get-aggregate-root[]
#PostMapping("/Posts")
Post newPost(#RequestBody Post newPost) {
return repository.save(newPost);
}
// Single item
#GetMapping("/Posts/{id}")
Optional<Post> one(#PathVariable Long id) {
return repository.findById(id);
// .orElseThrow(() -> new PostNotFoundException(id));
}
#PutMapping("/Posts/{id}")
Post replacePost(#RequestBody Post newPost, #PathVariable Long id) {
return repository.findById(id)
.map(Post -> {
Post.setCategory(newPost.getCategory());
Post.setTitle(newPost.getTitle());
Post.setContent(newPost.getContent());
return repository.save(Post);
})
.orElseGet(() -> {
newPost.setId(id);
return repository.save(newPost);
});
}
#DeleteMapping("/Posts/{id}")
void deletePost(#PathVariable Long id) {
repository.deleteById(id);
}
}
Post.java
package com.open.forum;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String category;
private String title;
private String content;
Post() {}
public Post(String category, String title, String content) {
this.category = category;
this.title = title;
this.content = content;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}

how can convert string into json result

I am using function to get the data as a tree format. the data return from GetDepotWithDepartment as a string datatype. how can I convert the string into JsonResult
public JsonResult GetDepotDepartemntsForMap()
{
string lson = _unitOfWork.Department.GetDepotWithDepartment();
return lson // error is coming cannot convert string into mvc.jsonREsult
}
The data coming from the repository linq as given
[
{
"id": 1,
"title": "1-Depot1",
"subs": [
{
"id": "1.1",
"title": "1.Advt"
},
{
"id": "1.2",
"title": "1.Admin"
}
]
},
{
"id": 2,
"title": "2-Depot2",
"subs": [
{
"id": "2.1",
"title": "2.Sales"
},
{
"id": "2.2",
"title": "2.Admin"
}
]
}
]
According to your description, I suggest you could try below codes to return the json result:
public JsonResult GetDepotDepartemntsForMap()
{
string lson = _unitOfWork.Department.GetDepotWithDepartment();
return new JsonResult(lson) // error is coming cannot convert string into mvc.jsonREsult
}
Result:

I have a model class in which when price type is "fixed" it return object and when it is "percentage" it return string, I don't know how to it?

I have a model which receive a response in which when i have option and furthur values, In values i have a priceType which can be percentage or fixed.
When pricetype is percentage the response is Sting but when price type is fixed the response id object and i receive error..
Accept response
JSONObject jsonResponse = new JSONObject(response);
Gson gson = new GsonBuilder().create();
product = gson.fromJson(String.valueOf(jsonResponse), Product.class);
JSON REsponse for Percentage
{
"id": 73,
"option_id": 21,
"price": "7.0000",
"price_type": "percent",
"position": 0,
"created_at": "2019-10-22 04:23:16",
"updated_at": "2019-10-30 04:57:45",
"label": "Red",
"translations": [
{
"id": 73,
"option_value_id": 73,
"locale": "en",
"label": "Red"
}
]
}
Json Response for Fixed Price
{
"id": 74,
"option_id": 21,
"price": {
"amount": "5.0000",
"formatted": "$5.00",
"currency": "USD"
},
"price_type": "fixed",
"position": 1,
"created_at": "2019-10-22 04:23:16",
"updated_at": "2019-10-22 04:23:16",
"label": "White",
"translations": [
{
"id": 74,
"option_value_id": 74,
"locale": "en",
"label": "White"
}
]
}
Price can either be class or String but i don't know how to put in my model class..
You can declare your pojo class like below :--
public class Product<T> {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getOption_id() {
return option_id;
}
public void setOption_id(int option_id) {
this.option_id = option_id;
}
private int id;
private int option_id;
public T getPrice() {
return price;
}
public void setPrice(T price) {
this.price = price;
}
T price;
.............
}
When response is parsed then you can check the type of the T then extract the data in that followed way.
Hope it solves your problem.Let me know if further code required.

Save entity which has one-to-one mapping in spring data rest

I have a very simple spring boot + spring data rest app. I tried to save the entity which has a one-to-one mapping using spring data rest but looks like only the parent is saved, the child is not.
Below is my code
#SpringBootApplication
public class Application{
#Autowired
private PersonRepository personRepo;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
CommandLineRunner init(){
Address address = new Address();
address.setCountry("US");
address.setState("SV");
Person person = new Person();
person.setName("Vincent");
person.setAddress(address);
personRepo.save(person);
return null;
}
}
#Entity
public class Address implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private int id;
private String country;
private String state;
}
#Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private int id;
private String name;
#OneToOne(cascade={CascadeType.ALL})
private Address address;
}
#Projection(name="inlineAddress",types={Person.class})
public interface InlineAddress {
String getName();
Address getAddress();
}
#RepositoryRestResource(excerptProjection=InlineAddress.class)
public interface PersonRepository extends JpaRepository<Person, Integer> {
Person findByName(#Param("name") String name);
Person findById(#Param("id") int id);
Page<Person> findByNameStartsWith(#Param("name") String name, Pageable page);
}
public interface AddressRepository extends JpaRepository<Address, Integer> {
}
After startup, if I visit http://localhost:8080/api/
I saw below response
{
_links: {
addresses: {
href: "http://localhost:8080/api/addresses{?page,size,sort}",
templated: true
},
persons: {
href: "http://localhost:8080/api/persons{?page,size,sort,projection}",
templated: true
},
profile: {
href: "http://localhost:8080/api/profile"
}
}
}
Then I visit http://localhost:8080/api/persons, so far every thing is good
{
"_embedded": {
"persons": [
{
"address": {
"country": "US",
"state": "SV"
},
"name": "Vincent",
"_links": {
"self": {
"href": "http://localhost:8080/api/persons/1"
},
"person": {
"href": "http://localhost:8080/api/persons/1{?projection}",
"templated": true
},
"address": {
"href": "http://localhost:8080/api/persons/1/address"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/persons"
},
"profile": {
"href": "http://localhost:8080/api/profile/persons"
},
"search": {
"href": "http://localhost:8080/api/persons/search"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
However, after I did a post http://localhost:8080/api/persons/ with
{
"name": "Michael",
"address": {
"country": "US",
"state": "SV"
}
}
It shows below, looks like the address is not get inserted for Michael
{
"_embedded": {
"persons": [
{
"address": {
"country": "US",
"state": "SV"
},
"name": "Vincent",
"_links": {
"self": {
"href": "http://localhost:8080/api/persons/1"
},
"person": {
"href": "http://localhost:8080/api/persons/1{?projection}",
"templated": true
},
"address": {
"href": "http://localhost:8080/api/persons/1/address"
}
}
},
{
"address": null,
"name": "Michael",
"_links": {
"self": {
"href": "http://localhost:8080/api/persons/2"
},
"person": {
"href": "http://localhost:8080/api/persons/2{?projection}",
"templated": true
},
"address": {
"href": "http://localhost:8080/api/persons/2/address"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/persons"
},
"profile": {
"href": "http://localhost:8080/api/profile/persons"
},
"search": {
"href": "http://localhost:8080/api/persons/search"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
Is there anything wrong with my code? I have tried using old approach without using spring data rest but using rest controller, the same json I posted was working fine. Not sure why spring data rest does not work here.
OK. Seems no way to do this. I have to first post a person by
{"name"="Michael"}
and then post an address by
{"country":"US,"state":"SV:} ,
and last put address to this person
{ "name":"Michael", "address":"localhost:8080/addresses/1" }