Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JacksonXmlProperty with attributes raises an exception when used with @JsonIdentityInfo #248

Open
lightbringer opened this issue Jun 29, 2017 · 4 comments

Comments

@lightbringer
Copy link

I'm running into an issue with version 2.8.9, which might be related to issue #81 / #82: I'm trying to serialize an object graph with a circular reference using JsonIdentityInfo. This works well unless the referenced object has properties marked with @JacksonXmlProperty( isAttribute = true )

Minimal example below:

@JsonIdentityInfo( generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id" )
@JacksonXmlRootElement( localName = "a" )
public class A {
    public static void main( String[] args ) throws IOException {
        final A a = new A();
        a.b = new B(); 
        a.b.setA( a ); //B has a reference back to A
        
        final XmlMapper mapper = new XmlMapper();
        final String json = mapper.writeValueAsString( a );
        System.out.println( json );
    }

    private B b;

    private String name = "test";

    
    @JacksonXmlProperty( isAttribute = true ) //test runs with isAttribute set to false (but serializes name as an element)
    public String getName() {
        return name;
    }
 //other getter/setter omitted for brevity    

Expected output:

<a name="test">
<id>1</id>
<b>
<a>1</a>
</b>
</a>

Instead the exception:

 Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Unexpected IOException (of type java.io.IOException): javax.xml.stream.XMLStreamException: Trying to write an attribute when there is no open start element.
	at com.fasterxml.jackson.databind.JsonMappingException.fromUnexpectedIOE(JsonMappingException.java:333)
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3077)
	at test.A.main(A.java:20)

is thrown. Removing the JsonIdentityInfo annotation solves the issue as well, i.e. name is correctly serialized as an attribute, but then a circular reference causes a stack overflow - obviously.

@j-stefani
Copy link

j-stefani commented Dec 19, 2017

I'm running into a similar issue (using v2.8.9) when trying to customize the PrettyPrinter and serialize to xml. If I simplify what I'm attempting:

ObjectMapper mapper = new XmlMapper(xmlModule);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
DefaultPrettyPrinter p = new DefaultPrettyPrinter();
ObjectWriter writer = mapper.writer(p);
writer.writeValueAsString(myObject);

then I run into the same exception. If I remove the use of the PrettyPrinter as such:

ObjectMapper mapper = new XmlMapper(xmlModule);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
ObjectWriter writer = mapper.writer();
writer.writeValueAsString(myObject);

then the serialization occurs without error. Maybe this will give further insight into the issue.

@cowtowncoder
Copy link
Member

Thank you. Yes, I think the whitespace causes issues here -- white space is a problem with XML because while it 99% of the time is meaningless, and users/devs rarely rely on it, it has to be retained and can not be discarded in general, without knowing it is safe to do so.
This means that XML parser will have to produce a text event, and then jackson xml module will try to figure out whether to drop it (most of the time), or retain for String value.

I don't know exact details of this specific case, but I am not surprised that this could be related.

@vmrfreitas
Copy link

So, did anyone figure out how to make the make a the XmlMapper print using the customized PrettyPrinter?

@cowtowncoder cowtowncoder added the to-evaluate Issue that has been received but not yet evaluated label Nov 14, 2020
@cowtowncoder cowtowncoder removed the to-evaluate Issue that has been received but not yet evaluated label Jul 7, 2021
@jdfischer-cedreo
Copy link

jdfischer-cedreo commented Jul 5, 2022

I found the answer, we need to use the Xml pretty printer and define a custom Xml indenter. An Indenter like the following would do the trick:

private static class MyIndenter
        implements DefaultXmlPrettyPrinter.Indenter, Serializable
    {

        private static final long serialVersionUID = 1L;
        @Override
        public void writeIndentation(final JsonGenerator p_jsonGenerator, final int p_level) throws IOException
        {
            p_jsonGenerator.writeRaw(new char[] { '\n' }, 0, 1);

            for (int l = 0; l < p_level; l++)
            {
                p_jsonGenerator.writeRaw(new char[] { ' ', ' ', ' ', ' ' }, 0, 4);
            }
        }
        @Override
        public void writeIndentation(final XMLStreamWriter2 p_xmlStreamWriter2, final int p_level) throws XMLStreamException
        {
            p_xmlStreamWriter2.writeRaw(new char[] { '\n' }, 0, 1);

            for (int l = 0; l < p_level; l++)
            {
                p_xmlStreamWriter2.writeRaw(new char[] { ' ', ' ', ' ', ' ' }, 0, 4);
            }
        }
        @Override
        public boolean isInline()
        {
            return false;
        }
    }

You can then use it in your XmlMapper:

final DefaultXmlPrettyPrinter xmlPrettyPrinter = new DefaultXmlPrettyPrinter();
final DefaultXmlPrettyPrinter.Indenter customIndenter = new MyIndenter();
xmlPrettyPrinter.indentObjectsWith(customIndenter);

new XmlMapper()
                         .writer(xmlPrettyPrinter)
                         .writeValueAsString(myObject)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants