/* * eXist Open Source Native XML Database * Copyright (C) 2013 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Id$ */ package org.exist.util.serializer.json; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class JSONObject extends JSONNode { private final static Logger LOG = LogManager.getLogger(JSONObject.class); protected JSONNode firstChild = null; private boolean asSimpleValue = false; public JSONObject() { super(Type.OBJECT_TYPE, ANONYMOUS_OBJECT); } public JSONObject(final String name) { super(Type.OBJECT_TYPE, name); } public JSONObject(final String name, final boolean asSimpleValue) { super(Type.OBJECT_TYPE, name); this.asSimpleValue = asSimpleValue; } public void addObject(final JSONNode node) { JSONNode childNode = findChild(node.getName()); if(childNode == null) { childNode = getLastChild(); if(childNode == null) { firstChild = node; } else { childNode.setNext(node); } } else { childNode.setNextOfSame(node); } } public JSONNode findChild(final String nameToFind) { JSONNode nextNode = firstChild; while(nextNode != null) { if(nextNode.getName().equals(nameToFind)) { return nextNode; } nextNode = nextNode.getNext(); } return null; } public JSONNode getLastChild() { JSONNode nextNode = firstChild; JSONNode currentNode = null; while(nextNode != null) { currentNode = nextNode; nextNode = currentNode.getNext(); } return currentNode; } public int getChildCount() { int count = 0; JSONNode nextNode = firstChild; while(nextNode != null) { count++; nextNode = nextNode.getNext(); } return count; } public void serialize(final Writer writer, final boolean isRoot) throws IOException { if(!isRoot && isNamed()) { writer.write('"'); writer.write(getName()); writer.write('"'); if(isIndent()) { writer.write(' '); } writer.write(':'); if(isIndent()) { writer.write(' '); } } if(getNextOfSame() != null || getSerializationType() == SerializationType.AS_ARRAY) { writer.write('['); JSONNode next = this; while(next != null) { next.serializeContent(writer); next = next.getNextOfSame(); if (next != null) { writer.write(','); if(isIndent()) { writer.write(' '); } } } writer.write(']'); } else { serializeContent(writer); } } @Override public void serializeContent(final Writer writer) throws IOException { if(firstChild == null) { // an empty node gets a null value, unless its a specified array if(getSerializationType() != SerializationType.AS_ARRAY) { writer.write("null"); } } else if(firstChild.getNext() == null && (firstChild.getType() == Type.VALUE_TYPE || (firstChild.isArray() && !firstChild.isNamed()))) { // if there's only one child and if it is text, it is serialized as simple value firstChild.serialize(writer, false); } else { // complex object writer.write('{'); if(isIndent()) { writer.write(' '); } JSONNode next = firstChild; boolean allowText = false; boolean skipMixedContentText = false; while(next != null) { if(next.getType() == Type.VALUE_TYPE) { /* if an element has attributes and text content, the text node is serialized as property "#text". Text in mixed content nodes is ignored though. */ if(allowText) { writer.write("\"#text\""); if(isIndent()) { writer.write(' '); } writer.write(':'); if(isIndent()) { writer.write(' '); } next.serialize(writer, false); allowText = false; } else { //writer.write("\"#mixed-content-text\" : "); skipMixedContentText = true; } } else { next.serialize(writer, false); } if(next.getType() == Type.SIMPLE_PROPERTY_TYPE) { allowText = true; } next = next.getNext(); if(next != null && !skipMixedContentText && !isMixedContentTextLast(next, allowText)) { writer.write(','); if(isIndent()) { writer.write(' '); } } skipMixedContentText = false; } if(isIndent()) { writer.write(' '); } writer.write('}'); } } private boolean isMixedContentTextLast(final JSONNode node, final boolean allowText) { return node.getType() == Type.VALUE_TYPE && !allowText && node.equals(getLastChild()); } @Override public String toString() { final StringWriter writer = new StringWriter(); try { serialize(writer, false); } catch(final IOException e) { LOG.warn(e); } return writer.toString(); } }